Difference between revisions of "Windows Powershell"

From Ever changing code
Jump to navigation Jump to search
m (Pio2pio moved page Windows\Powershell to Windows Powershell without leaving a redirect: removed slash from the title)
 
(46 intermediate revisions by the same user not shown)
Line 1: Line 1:
== Generals ==
= ISE - PowerShell IDE =
<tt>Untitled.ps1</tt> - when working in ISEv2 or v4 in a script edit section, you can select code and press F8 to execute it <br>
<tt>Untitled.ps1</tt> - when working in ISEv2 or v4 in a script edit section, you can select code and press F8 to execute it <br>
<tt>$_</tt> - substitute a singular record in array <br>
<tt>$_</tt> - substitute a singular record in array <br>
<tt>-eq, -or -and</tt> - dash means operator <br>
<tt>-eq, -or -and</tt> - dash means operator <br>


== Modules ==
= Test network connection =
Testing from PowerShell if NFS port on remote server is open. The test takes around 5 seconds, them prints output.
<source>
PS C:\> Test-NetConnection -ComputerName 10.1.20.1 -Port 2049
ComputerName          : 10.1.20.1
RemoteAddress          : 10.1.20.1
RemotePort            : 2049
InterfaceAlias        : Ethernet
SourceAddress          : 10.1.10.111
PingSucceeded          : True
PingReplyDetails (RTT) : 2 ms
TcpTestSucceeded      : True
</source>
 
= PowerShell of linux commands =
;Tail
<source lang="bash">
PS1 C:\> Get-Content -Path "C:\scripts\test.log" -Wait -Tail 100
$ tail -f /mnt/c/scripts/test.log
</source>
 
;Curl
<source lang="bash">
PS1 C:\> (Invoke-WebRequest -Headers @{"Host"="api.example.com"} -Uri http://127.0.0.1:8080 -UseBasicParsing).statuscode
PS1 C:\>  Invoke-RestMethod -Headers @{"Host"="api.example.com"} -Uri http://127.0.0.1/healthcheck                                 
$ curl --header 'Host: api.example.com' http://127.0.0.1/
</source>
 
;Wget
<source lang="bash">
#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 https://github.com/Example/package.zip -OutFile package.zip
$ wget https://github.com/Example/package.zip -OutFile package.zip
</source>
;Troubleshooting:Error: ''The request was aborted: Could not create SSL/TLS secure channel.'' - set SecurityProtocol
= Set a hostname =
<syntaxhighlightjs lang="powershell">
# 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
</syntaxhighlightjs>
 
= Windows features =
== List installed features ==
<syntaxhighlightjs lang="powershell">
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
</syntaxhighlightjs>
 
== Install Windows feature ==
=== nfs-client ===
{{note|[https://docs.aws.amazon.com/efs/latest/ug/limits.html EFS doesn't support mounting from Windows EC2 instances] although there is work by www.citi.umich.edu [http://www.citi.umich.edu/projects/nfsv4/windows/readme.html nfsv4/windows] alternative or this project [https://github.com/kofemann/ms-nfs41-client ms-nfs41-client] to compile your own client }}
 
 
<syntaxhighlightjs lang="powershell">
# example based on Windows 2012 R2
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}
</syntaxhighlightjs>
 
 
Access the exported location, once the client is installed and NFS exports available on the network:
<syntaxhighlightjs lang="powershell">
# File explorer, once discovered network drive will be showed with the label of the NFS exported directory
\\10.1.1.1 # eg. label '\nas'
</syntaxhighlightjs>
 
 
Other features, confirmed Windows Server Base 2019 | AWS
<syntaxhighlightjs lang="powershell">
Install-WindowsFeature -name Web-Server -IncludeManagementTools #IIS
</syntaxhighlightjs>
 
=== [[ Hyper-V#Powershell_install_Hyper-V_feature ]]===
 
= Download a file =
== Bits transfer ==
Windows has background service to download files. The admin console is called <code>bitsadmin</code> where equivalent Powershell cmdlets are <code>*-BitsTransfer</code>
<source>
$tmpPath="C:\tmp"
If (-Not (Test-Path $tmpPath -pathType container)) { md $tmpPath }
$tentacle_url="https://octopus.com/downloads/latest/WindowsX64/OctopusTentacle"
$tentacle_file="tentacle_install.msi"
Start-BitsTransfer -Source $tentacle_url -Destination $tmpPath\$tentacle_file
WriteOutput "BitsTransfer - success: $?"
</source>
 
 
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."
 
== System.Net.WebClient ==
A common .NET class used for downloading files is the System.Net.WebClient class.
<source>
$WebClient = New-Object System.Net.WebClient
$WebClient.DownloadFile("http://mirror.filearena.net/pub/speed/SpeedTest_128MB.dat","C:\tmp\file.zip")
 
#Or one-liner
$url="http://mirror.filearena.net/pub/speed/SpeedTest_128MB.dat"
$file="C:\tmp\file.zip"
(New-Object System.Net.WebClient).DownloadFile($url, $file)
</source>
 
= Install an application =
This is usually done using <code>msiexec</code> 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 <code>start</code> or Powershell cmdlet <code>Start-Process</code> with <code>-Wait</code> option. It will start separate process (for eg. software installation) and wait until completed.
<source>
$tmpPath="C:\tmp"
$splunk_forwarder_file=""
Start-Process msiexec.exe -ArgumentList "/i $tmpPath\$splunk_forwarder_file RECEIVING_INDEXER=splunk.acme.com:9997 DEPLOYMENT_SERVER=splunk.acme.com:8089 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
</source>
 
 
Please pay attention to a way how arguments to application <code>msiexec.exe</code> have been passed in <code>"/i app.exe apparg1 apparg2", "/quiet"</code>
== References ==
*[https://stackoverflow.com/questions/39407004/positional-parameter-error-in-powershell-script Positional error in PS scripts]
 
= Modules =
Load ActiveDirectory module to have access to AD, pre-requirement to execute most commands below
Load ActiveDirectory module to have access to AD, pre-requirement to execute most commands below
  Import-Module -Name ActiveDirectory
  Import-Module -Name ActiveDirectory


== Extract from Active Directory ==
= Create a big file =
get-aduser -Filter {Samaccountname -eq "Smith"} -properties Organization
<source>
get-aduser -Filter {(Givenname -eq "Smithy") -and (Surname -eq "Smith")}  
fsutil file createnew big1k 1024      # 1Kb, filled in with Null
fsutil file createnew big1g 1073741824 # 1Gb
</source>
 
= Schedule task =
Create a schedule task that runs on a powershell script on boot, then disables itself.
<source>
$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
$localIP=(Invoke-RestMethod http://169.254.169.254/latest/meta-data/local-ipv4)
.\Tentacle.exe register-with  --instance $env:computername `
  --server "https://deploy.contactengine.net" --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


Build array <tt>$users</tt> with all Samaccountname(loginnames) with additional properties: Name, Description
#Disable/delete scheduled task
$users = get-aduser -Filter {Samaccountname -like "*"} -properties Name, Description
#Unregister-ScheduledTask -TaskName "RegisterTentacle" -Confirm:$false
Disable-ScheduledTask    -TaskName "RegisterTentacle"
'@
</source>


Return array object count
What is worth to note re-direction <code>-Argument '-NoProfile -WindowStyle Hidden -NoLogo -NonInteractive -c "powershell .\TentacleRegister.ps1 -verbose >> .\TentacleRegisterScheduleTask.log 2>&1"'</code> 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 <tt>RegisterTentacle</tt>
$users.count


Search array $users where <tt>$_</tt> each object in array field <tt>samaccountname</tt> has a given string
= Extract from Active Directory =
$users | Where-Object {$_.samaccountname -eq "string_to_compare"}
<syntaxhighlightjs lang="powershell">
get-aduser -Filter {Samaccountname -eq "Smith"} -properties Organization
get-aduser -Filter {(Givenname -eq "Smithy") -and (Surname -eq "Smith")}  


Build array of enabled and disabled accounts in AD where field Enabled equal $true ($true boolean is 1 $false is 0)
# Build array $users with all Samaccountname(loginnames) with additional properties: Name, Description
$enabledusers = $users | Where-Object {$_.Enabled -eq $true}
$users = get-aduser -Filter {Samaccountname -like "*"} -properties Name, Description
$disabledusers = $users | Where-Object {$_.Enabled -eq $false}


Filter array <tt>$disabledusers</tt> returning only <tt>Samaccountname, GivenName, Surname</tt> and display (ft = Format-table)
# Return array object count
$disabledusers | Select-Object Samaccountname, GivenName, Surname | ft -AutoSize
$users.count


Build create new array from filter of $users array if name or description contains a string
# Search array $users where $_ each object in array field samaccountname has a given string
$aausers = $users | Where-Object {( $_.Name -like "*aa*") -or ($_.Description -like "*bb*")}
$users | Where-Object {$_.samaccountname -eq "string_to_compare"}
$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
# Build array of enabled and disabled accounts in AD where field Enabled equal $true ($true boolean is 1 $false is 0)
foreach ($aauser in $aausers)
$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))})  
   $realuser = [array](get-aduser -Filter {((Givenname -eq $aauser.Givenname) -and (Surname -eq $aauser.Surname))})  
   write-host $aauser.samaccountname "|" $aauser.name "|"  $aauser.enabled "|"$realuser[0].SamAccountname "|"$realuser[0].GivenName"|" $realuser[0].Surname"|" $realuser[0].Enabled
   write-host $aauser.samaccountname "|" $aauser.name "|"  $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)
# 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}
$temp = Get-ADUser -Properties GivenName, Surname -filter {Enabled -eq $false}


Export the array to CSV file
# Export the array to CSV file
$temp | Export-Csv temp.csv
$temp | Export-Csv temp.csv
</syntaxhighlightjs>


= AD Extract 2 =
= AD Extract 2 =
Line 54: Line 240:
= Create users from csv =
= Create users from csv =
Csv file <tt>BulkAddADUsers.csv</tt>
Csv file <tt>BulkAddADUsers.csv</tt>
Name,GivenName,Surname,SamAccountName,UserPrincipalName,EmailAddress,AccountEnabled,AccountPassword,PasswordNeverExpires,Path
<source>
Full Name,Firstname,Surname,fsurname,fsurname@example.com,fsurname@example.com,$true,PassWord123,$true,"OU=Users,OU=Testing ,OU=USA,DC=corp-example,DC=io"
Name,GivenName,Surname,SamAccountName,UserPrincipalName,EmailAddress,AccountEnabled,AccountPassword,PasswordNeverExpires,Path
Full Name,Firstname,Surname,fsurname,fsurname@example.com,fsurname@example.com,$true,PassWord123,$true,"OU=Users,OU=Testing ,OU=USA,DC=corp-example,DC=io"
</source>


<tt>BulkAddADUsers.ps1</tt>
<tt>BulkAddADUsers.ps1</tt>
# CSV headline: Name,GivenName,Surname,SamAccountName,UserPrincipalName,EmailAddress,Enabled,AccountPassword,PasswordNeverExpires,Path
<syntaxhighlightjs lang="powershell">
# Script            - CSV headline
# CSV headline: Name,GivenName,Surname,SamAccountName,UserPrincipalName,EmailAddress,Enabled,AccountPassword,PasswordNeverExpires,Path
# Name              - first+last name
# Script            - CSV headline
# GivenName        - first name
# Name              - first+last name
# Surname          - last name
# GivenName        - first name
# SamAccountName    - username
# Surname          - last name
# UserPrincipalName - it is user-logon-name, where you need to choose domain, eg. test@example.com or @corp-example.io
# SamAccountName    - username
# Path - object location, use get-aduser <SamAccountName>
# UserPrincipalName - it is user-logon-name, where you need to choose domain, eg. test@example.com or @corp-example.io
# Path - object location, use get-aduser <SamAccountName>
Import-Csv .\BulkAddADUsers.csv | % { `
 
New-ADUser -Name $_.Name -GivenName $_.GivenName -Surname $_.Surname -SamAccountName $_.SamAccountName `
Import-Csv .\BulkAddADUsers.csv | % { `
-UserPrincipalName $_.UserPrincipalName -EmailAddress $_.EmailAddress `
New-ADUser -Name $_.Name -GivenName $_.GivenName -Surname $_.Surname -SamAccountName $_.SamAccountName `
-Enabled $true -AccountPassword (ConvertTo-SecureString $_.AccountPassword -AsPlainText -force) `
  -UserPrincipalName $_.UserPrincipalName -EmailAddress $_.EmailAddress `
-PasswordNeverExpires $true -Path $_.Path }
  -Enabled $true -AccountPassword (ConvertTo-SecureString $_.AccountPassword -AsPlainText -force) `
  -PasswordNeverExpires $true -Path $_.Path
#errors
}
#-Enabled cannot read $true value from CSV therefore it has been hard coded
#errors
#-Enabled cannot read $true value from CSV therefore it has been hard coded
</syntaxhighlightjs>


= Get membership of a user =
= Get membership of a user =
  Get-ADPrincipalGroupMembership username| select name
  Get-ADPrincipalGroupMembership username| select name


= IIS =
== create a website ==
Tested on Server 2012 R2 Data Centre in Azure
<syntaxhighlightjs lang="powershell">
$SiteName    = "WWW"
$AppPoolName = "WWWAppPool"
$SiteFolder  = Join-Path -Path 'C:\inetpub\wwwroot' -ChildPath $SiteName
$LogDir      = "d:\Logs\iis_logs\$SiteName"
$HostHeader  = "www.example.com"
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 logFile.directory -value $LogDir
Start-WebSite  -Name $SiteName
</syntaxhighlightjs>
== rewrite rules [http://www.iis.net/learn/extensions/url-rewrite-module/url-rewrite-module-configuration-reference#Using_back-references_in_rewrite_rules using back references] ==
The rewrite module for IIS can be used to execute 2 actions:
* <code>rewrite</code> - the URL stays the same but the content is loaded from somewhere else
* <code>redirects</code> - when the user’s browser is taken to a new URL
Regex pattern capture groups in <code><rule ..><match url="^(.*)$ />"</rule></code> can be reused in your actions. These are called '''back references'''. As per docs: 'When an ECMAScript pattern syntax is used, a back-reference can be created by putting parenthesis around the part of the pattern that must capture the back-reference.'
Example regex pattern:
<source>
^(www\.)(.*)$
</source>
And using the input string <code>www.foo.com</code> in the conditions, you will have:
<source>
{C:0} - www.foo.com
{C:1} - www.
{C:2} - foo.com
</source>
To make it simple:
* <code>{R:x}</code> is used as back reference from the rule pattern (<code><match url="..."></code>).
* <code>{C:x}</code> is used as back reference from the condition pattern (<code><conditions><add input="{HTTP_HOST}" pattern="..."></conditions></code>)
* The <code>0</code> reference contains the whole input string
* The <code>1</code> reference will contain the first part of the string matching the pattern in the first parenthesis <code>()</code>, the <code>2</code> reference the second one, etc...up to the reference number <code>9</code>
{{Note|When "Wildcard" pattern syntax is used, the back-references are always created when an asterisk symbol (*) is used in the pattern. No back-references are created when "?" is used in the pattern.}}
{{Note|Important: The rule is only applied to the path; don’t let the name url fool you. (for example, in <code>example.com/test</code>, the scheme and domain name are ignored for the <code>url</code> matching)", and 'Always remember when you debug a redirect (specifically a 301) that browsers tend to cache them and that it can lead to frustration when you change the rule but nothing happens'}}
;References
*[https://nicolas.guelpa.me/blog/2015/02/21/rewrite-redirect-iis.html rewrite-redirect-iis]
*[https://stackoverflow.com/questions/16998832/iis-url-rewrite-rn-clarification IIS URL Rewrite {R:N} clarification]
= File content =
<syntaxhighlightjs lang="powershell">
$hostsFileContent = @"
127.0.0.1 example.com
127.0.0.1 example.local
"@
Add-Content -Path "c:\Windows\System32\drivers\etc\hosts" -Value $hostsFileContent
Set-Content -Path "c:\Windows\System32\drivers\etc\hosts" -Value $hostsFileContent
</syntaxhighlightjs>
= References =
= References =


[[Category:Windows]]
[[Category:Windows]][[Category:Powershell]]

Latest revision as of 14:16, 4 November 2020

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 10.1.20.1 -Port 2049
ComputerName           : 10.1.20.1
RemoteAddress          : 10.1.20.1
RemotePort             : 2049
InterfaceAlias         : Ethernet
SourceAddress          : 10.1.10.111
PingSucceeded          : True
PingReplyDetails (RTT) : 2 ms
TcpTestSucceeded       : True

PowerShell of linux commands

Tail
PS1 C:\> Get-Content -Path "C:\scripts\test.log" -Wait -Tail 100
$ tail -f /mnt/c/scripts/test.log
Curl
PS1 C:\> (Invoke-WebRequest -Headers @{"Host"="api.example.com"} -Uri http://127.0.0.1:8080 -UseBasicParsing).statuscode
PS1 C:\>  Invoke-RestMethod -Headers @{"Host"="api.example.com"} -Uri http://127.0.0.1/healthcheck                                   
$ curl --header 'Host: api.example.com' http://127.0.0.1/
Wget
#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 https://github.com/Example/package.zip -OutFile package.zip
$ wget https://github.com/Example/package.zip -OutFile package.zip
Troubleshooting
Error: The request was aborted: Could not create SSL/TLS secure channel. - set SecurityProtocol

Set a hostname

<syntaxhighlightjs lang="powershell">

  1. 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 </syntaxhighlightjs>

Windows features

List installed features

<syntaxhighlightjs lang="powershell"> 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 </syntaxhighlightjs>

Install Windows feature

nfs-client

Note: EFS doesn't support mounting from Windows EC2 instances although there is work by www.citi.umich.edu nfsv4/windows alternative or this project ms-nfs41-client to compile your own client


<syntaxhighlightjs lang="powershell">

  1. example based on Windows 2012 R2

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} </syntaxhighlightjs>


Access the exported location, once the client is installed and NFS exports available on the network: <syntaxhighlightjs lang="powershell">

  1. File explorer, once discovered network drive will be showed with the label of the NFS exported directory

\\10.1.1.1 # eg. label '\nas' </syntaxhighlightjs>


Other features, confirmed Windows Server Base 2019 | AWS <syntaxhighlightjs lang="powershell"> Install-WindowsFeature -name Web-Server -IncludeManagementTools #IIS </syntaxhighlightjs>

Hyper-V#Powershell_install_Hyper-V_feature

Download a file

Bits transfer

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

$tmpPath="C:\tmp"
If (-Not (Test-Path $tmpPath -pathType container)) { md $tmpPath }
$tentacle_url="https://octopus.com/downloads/latest/WindowsX64/OctopusTentacle"
$tentacle_file="tentacle_install.msi"
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."

System.Net.WebClient

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

$WebClient = New-Object System.Net.WebClient
$WebClient.DownloadFile("http://mirror.filearena.net/pub/speed/SpeedTest_128MB.dat","C:\tmp\file.zip")

#Or one-liner
$url="http://mirror.filearena.net/pub/speed/SpeedTest_128MB.dat"
$file="C:\tmp\file.zip"
(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.

$tmpPath="C:\tmp"
$splunk_forwarder_file=""
Start-Process msiexec.exe -ArgumentList "/i $tmpPath\$splunk_forwarder_file RECEIVING_INDEXER=splunk.acme.com:9997 DEPLOYMENT_SERVER=splunk.acme.com:8089 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"

References

Modules

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
$localIP=(Invoke-RestMethod http://169.254.169.254/latest/meta-data/local-ipv4)
.\Tentacle.exe register-with   --instance $env:computername `
   --server "https://deploy.contactengine.net" --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

<syntaxhighlightjs lang="powershell"> get-aduser -Filter {Samaccountname -eq "Smith"} -properties Organization get-aduser -Filter {(Givenname -eq "Smithy") -and (Surname -eq "Smith")}

  1. Build array $users with all Samaccountname(loginnames) with additional properties: Name, Description

$users = get-aduser -Filter {Samaccountname -like "*"} -properties Name, Description

  1. Return array object count

$users.count

  1. Search array $users where $_ each object in array field samaccountname has a given string

$users | Where-Object {$_.samaccountname -eq "string_to_compare"}

  1. 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}

  1. Filter array $disabledusers returning only Samaccountname, GivenName, Surname and display (ft = Format-table)

$disabledusers | Select-Object Samaccountname, GivenName, Surname | ft -AutoSize

  1. 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

  1. 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.name "|"  $aauser.enabled "|"$realuser[0].SamAccountname "|"$realuser[0].GivenName"|" $realuser[0].Surname"|" $realuser[0].Enabled

}

  1. 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}

  1. Export the array to CSV file

$temp | Export-Csv temp.csv </syntaxhighlightjs>

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

Name,GivenName,Surname,SamAccountName,UserPrincipalName,EmailAddress,AccountEnabled,AccountPassword,PasswordNeverExpires,Path
Full Name,Firstname,Surname,fsurname,fsurname@example.com,fsurname@example.com,$true,PassWord123,$true,"OU=Users,OU=Testing ,OU=USA,DC=corp-example,DC=io"

BulkAddADUsers.ps1 <syntaxhighlightjs lang="powershell">

  1. CSV headline: Name,GivenName,Surname,SamAccountName,UserPrincipalName,EmailAddress,Enabled,AccountPassword,PasswordNeverExpires,Path
  2. Script - CSV headline
  3. Name - first+last name
  4. GivenName - first name
  5. Surname - last name
  6. SamAccountName - username
  7. UserPrincipalName - it is user-logon-name, where you need to choose domain, eg. test@example.com or @corp-example.io
  8. 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

}

  1. errors
  2. -Enabled cannot read $true value from CSV therefore it has been hard coded

</syntaxhighlightjs>

Get membership of a user

Get-ADPrincipalGroupMembership username| select name

IIS

create a website

Tested on Server 2012 R2 Data Centre in Azure <syntaxhighlightjs lang="powershell"> $SiteName = "WWW" $AppPoolName = "WWWAppPool" $SiteFolder = Join-Path -Path 'C:\inetpub\wwwroot' -ChildPath $SiteName $LogDir = "d:\Logs\iis_logs\$SiteName" $HostHeader = "www.example.com"

Import-Module WebAdministration

  1. create appPool

if(-Not (Test-Path IIS:\AppPools\$AppPoolName)) { New-WebAppPool -Name $AppPoolName -Force }

  1. 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

  1. Logging dir

if (-Not (Test-Path $LogDir -pathType container)) { md $LogDir } Set-ItemProperty "IIS:\Sites\$SiteName" -name logFile.directory -value $LogDir Start-WebSite -Name $SiteName </syntaxhighlightjs>

rewrite rules using back references

The rewrite module for IIS can be used to execute 2 actions:

  • rewrite - the URL stays the same but the content is loaded from somewhere else
  • redirects - when the user’s browser is taken to a new URL


Regex pattern capture groups in <rule ..><match url="^(.*)$ />"</rule> can be reused in your actions. These are called back references. As per docs: 'When an ECMAScript pattern syntax is used, a back-reference can be created by putting parenthesis around the part of the pattern that must capture the back-reference.'


Example regex pattern:

^(www\.)(.*)$


And using the input string www.foo.com in the conditions, you will have:

{C:0} - www.foo.com
{C:1} - www.
{C:2} - foo.com


To make it simple:

  • {R:x} is used as back reference from the rule pattern (<match url="...">).
  • {C:x} is used as back reference from the condition pattern (<conditions><add input="{HTTP_HOST}" pattern="..."></conditions>)
  • The 0 reference contains the whole input string
  • The 1 reference will contain the first part of the string matching the pattern in the first parenthesis (), the 2 reference the second one, etc...up to the reference number 9


Note: When "Wildcard" pattern syntax is used, the back-references are always created when an asterisk symbol (*) is used in the pattern. No back-references are created when "?" is used in the pattern.


Note: Important: The rule is only applied to the path; don’t let the name url fool you. (for example, in example.com/test, the scheme and domain name are ignored for the url matching)", and 'Always remember when you debug a redirect (specifically a 301) that browsers tend to cache them and that it can lead to frustration when you change the rule but nothing happens'


References

File content

<syntaxhighlightjs lang="powershell"> $hostsFileContent = @" 127.0.0.1 example.com 127.0.0.1 example.local "@

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

References