Powershell script - Organize script by functions - powershell-2.0

When I put the Function {} around my powershell script, to change the registry key, the script doesn't seem to run within the brackets. When I take out the Function {}, the script runs fine. What am I missing here?
Function ClearPageFile
{
$regpath = "HKLM:\SYSTEM....(rest of path)"
$key = "ClearPageFileAtShutdown
Set-ItemProperty -Path $regpath -Name $key -Value 0
}

Unless you are calling the function some place else, here's your answer...
Function ClearPageFile
{
$regpath = "HKLM:\SYSTEM....(rest of path)"
$key = "ClearPageFileAtShutdown
Set-ItemProperty -Path $regpath -Name $key -Value 0
}
#call the function
ClearPageFile

Related

What REST Api return AGENT.BUILDDIRECTORY value for the given pool in Azure DevOps Server on-prem?

So I fetched the build definition:
$bd = ...
It has the pool Id:
C:\> $bd.queue.pool.id
90
C:\>
Now I can get the pool data with the capabilities using the Url https://myserver.com/tfs/DefaultCollection/_apis/distributedtask/pools/90/agents?includeCapabilities=true
And I do get a lot of information, but Agent.BuildDirectory is not there:
C:\> $x = Invoke-RestMethod $Url -UseDefaultCredentials
C:\> $x.value.systemCapabilities[0].PSObject.Properties.Name |? { $_ -match '^Agent' }
Agent.Name
Agent.Version
Agent.ComputerName
Agent.HomeDirectory
Agent.OS
Agent.OSArchitecture
Agent.OSVersion
C:\>
Given a valid build definition how can I get Agent.BuildDirectory for every on-prem agent associated with it using the rest API?
If you access the capabilities of the agent from UI, you'll find there is no Agent.BuildDirectory capability, so you can not get it from REST API. Also, Agent.BuildDirectory is the local path on the agent where all folders for a given build pipeline are created, the format is like c:\agent_work\_work\1, so it's different between build pipelines.
You could get this variable from the build pipeline, or check the build log to get the path.
I ended up with the following function:
function Invoke-ForEachBuildAgent(
[Parameter(Mandatory)]$BuildDefinition,
[Parameter(Mandatory)][scriptblock]$Action,
[pscredential]$Credential
)
{
function MapShare($AgentComputerName, $RemoteDirectory, $PSDrive, [pscredential]$Credential)
{
if ($Credential)
{
$Share = "\\$AgentComputerName\$($RemoteDirectory[0])`$"
$Mapped = $null
try
{
$Mapped = Test-Path $Share
}
catch
{
}
if (!$Mapped)
{
New-PSDrive $PSDrive FileSystem $Share -Credential $Credential > $null
}
}
}
$RelSourceFolderPath = "SourceRootMapping\1a33a8ed-f8fd-4c08-9f44-440ea9f20315\$($BuildDefinition.Id)\SourceFolder.json"
$PSDrive = [guid]::NewGuid().ToString('N')
if (!$Credential -and ($env:USERNAME -ne INSERT_THE_DEFAULT_USER_NAME_HERE))
{
$Credential = Get-Credential -UserName INSERT_THE_DEFAULT_USER_NAME_WITH_DOMAIN_HERE -Message "Login to access the agents"
}
$Url = "$TfsInstanceUrl/_apis/distributedtask/pools/$($BuildDefinition.Queue.Pool.Id)/agents?includeCapabilities=true"
(Invoke-RestMethod $Url -UseDefaultCredentials).value.systemCapabilities | Where-Object {
$_
} | ForEach-Object {
try
{
$AgentComputerName = $_.'Agent.ComputerName'
$AgentHomeDirectory = $_.'Agent.HomeDirectory'
MapShare $AgentComputerName $AgentHomeDirectory $PSDrive $Credential
$Path = "\\$AgentComputerName\$AgentHomeDirectory\.agent".Replace(':', '$')
$AgentBuildDirectoryRoot = (Get-Content $Path | ConvertFrom-Json).workFolder
if ($AgentBuildDirectoryRoot[0] -ne $AgentHomeDirectory[0])
{
Remove-PSDrive $PSDrive -ErrorAction SilentlyContinue
MapShare $AgentComputerName $AgentBuildDirectoryRoot $PSDrive $Credential
}
$Path = "\\$AgentComputerName\$AgentBuildDirectoryRoot\$RelSourceFolderPath".Replace(':', '$')
if (!(Test-Path $Path))
{
return
}
$BuildFolderNumber = (Get-Content $Path | ConvertFrom-Json).agent_builddirectory
$AgentBuildDirectory = [io.path]::GetFullPath("$Path\..\..\..\..\$BuildFolderNumber")
& $Action -SystemCapabilities $_ `
-UncAgentBuildDirectory $AgentBuildDirectory `
-AgentBuildDirectory "$AgentBuildDirectoryRoot\$LastBuildFolderNumber" `
-Credential $Credential
}
catch
{
$_.Exception
}
finally
{
Remove-PSDrive $PSDrive -ErrorAction SilentlyContinue
}
}
}
I am pretty sure it only works for on-prem agents. I will update my answer. Those running agents hosted in Azure do not really need this functionality in the first place.

How do I enable TLS 1.0 and TLS 1.1 in ASP.NET MVC application running on Windows Server 2019?

I have an ASP.NET MVC web service hosted in Microsoft Azure Cloud Services (as a web role) currently targeting .NET Framework 4.5.2 and configured to run on Windows Server 2012. I need to migrate it to .NET Framework 4.7.2 and Windows Server 2019. All goes just fine except...
Windows Server 2012 is configured such that IIS allows TLS 1.0, TLS 1.1 and TLS 1.2 by default but Windows Server 2019 has IIS configured to only allow TLS 1.2 This may break some of the clients so I'd like to temporarily enable TLS 1.0 and 1.1 in Windows 2019 and then later talk to the clients and disable all but TLS 1.2
I found this answer which suggests that I change the registry keys
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server]
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client]
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server]
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client]
and put
"Enabled"=dword:ffffffff
"DisabledByDefault"=dword:00000000
in there. (I also tried dword:00000001 instead of dword:ffffffff - no difference) I included this as a startup task such that necessary changes are imported into the registry.
It doesn't help. I use https://www.ssllabs.com/ssltest to check the available TLS modes. It says only TLS 1.2 is allowed both before and after the change. It properly showed that 1.0, 1.1 and 1.2 were available for Windows Server 2012.
How do I have TLS 1.0 and 1.1 enabled?
Add the cipher suites for TLS 1.0/1.1. Complete script (run as admin):
$suites = #(
'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256',
'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384',
'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256' ,
'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384' ,
'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256',
'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384',
'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256' ,
'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384' ,
'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA' ,
'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA' ,
'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA' ,
'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA' ,
'TLS_RSA_WITH_AES_256_GCM_SHA384' ,
'TLS_RSA_WITH_AES_128_GCM_SHA256' ,
'TLS_RSA_WITH_AES_256_CBC_SHA256' ,
'TLS_RSA_WITH_AES_128_CBC_SHA256' ,
'TLS_RSA_WITH_AES_256_CBC_SHA' ,
'TLS_RSA_WITH_AES_128_CBC_SHA'
)
$registry = #(
#{Path='HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server';Name='DisabledByDefault';Type='DWord';Value=0},
#{Path='HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server';Name='Enabled';Type='DWord';Value=1},
#{Path='HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server';Name='DisabledByDefault';Type='DWord';Value=0},
#{Path='HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server';Name='Enabled';Type='DWord';Value=1},
#{Path='HKLM:\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL\00010002';Name='Functions';Type='String';Value=$suites -Join ','}
)
$reboot = $null
foreach ($row in $registry) {
if (-Not(Test-Path $row.Path)) {
New-Item -Path $row.Path -Force | Out-Null
}
try {
$val = Get-ItemPropertyValue -Path $row.Path -Name $row.Name
} catch {
$val = $null
}
if ($val -ne $row.Value) {
$reboot = $true
Set-ItemProperty -Path $row.Path -Name $row.Name -Type $row.Type -Value $row.Value -Force
Write-Host "$($row.Path)!$($row.Name)=$($row.Value)"
}
}
if ($reboot -eq $true) {
Write-Host "Rebooting now..."
shutdown.exe /r /t 0 /c "Rebooting for registry changes to take effect" /f /d p:2:4
}
You could use below Powershell script to enable tls 1.0 and 1.1:
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[ValidateSet("SSL30","TLS10","TLS11","TLS12")]
[string]$Proto,
[ValidateSet("Client","Server")]
[string]$Target,
[Parameter(Mandatory=$True)]
[ValidateSet("Enable","Disable")]
$Action)
Function CheckKey{
param(
[string]$Proto
)
$RegKey = $null
switch ($Proto){
SSL30 {$RegKey = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0"}
TLS10 {$RegKey = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0"}
TLS11 {$RegKey = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1"}
TLS12 {$RegKey = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2"}
default{"Not supported protocol. Possible values: SSL30, TLS10, TLS11, TLS12"
exit}
}
return $Regkey
}
$RegKey = CheckKey -Proto $Proto
[string[]]$TargetKey = $null
if(!($Target)){
Write-Host "Setting up both Client and Server protocols"
$TargetKey = $(Join-Path $RegKey "Client").ToString()
$TargetKey += $(Join-Path $RegKey "Server").ToString()
if(!(Test-path -Path $TargetKey[0])){
New-Item $TargetKey[0] -Force
}
if(!(Test-path -Path $TargetKey[1])){
New-Item $TargetKey[1] -Force
}
}
else{
Write-Host "Setting up $Target protocols"
$TargetKey = $(Join-Path $RegKey $Target).ToString()
if(!(Test-path -Path $(Join-Path $RegKey $Target))){
New-Item $TargetKey -Force
}
}
Function SetProto{
param(
[string[]]$TargetKey,
[string]$Action
)
foreach($key in $TargetKey){
try{
Get-ItemProperty -Path $key -Name "Enabled" -ErrorAction Stop | Out-Null
if($Action -eq "Disable"){
Write-Host "`t`Updating $key"
Set-ItemProperty -Path $key -Name "Enabled" -Value 0 -Type "DWord"
}
else{
Write-Host "`t`Updating $key"
Set-ItemProperty -Path $key -Name "Enabled" -Value 1 -Type "DWord"
}
}Catch [System.Management.Automation.PSArgumentException]{
if($Action -eq "Disable"){
Write-Host "`t`Creating $key"
New-ItemProperty -Path $key -Name "Enabled" -Value 0 -PropertyType "DWord"
}
else{
Write-Host "`t`Creating $key"
New-ItemProperty -Path $key -Name "Enabled" -Value 1 -PropertyType "DWord"
}
}
try{
Get-ItemProperty -Path $key -Name "DisabledByDefault" -ErrorAction Stop | Out-Null
if($Action -eq "Disable"){
Write-Host "`t`Updating $key"
Set-ItemProperty -Path $key -Name "DisabledByDefault" -Value 1 -Type "DWord"
}
else{
Write-Host "`t`Updating $key"
Set-ItemProperty -Path $key -Name "DisabledByDefault" -Value 0 -Type "DWord"
}
}Catch [System.Management.Automation.PSArgumentException]{
if($Action -eq "Disable"){
Write-Host "`t`Creating $key"
New-ItemProperty -Path $key -Name "DisabledByDefault" -Value 1 -PropertyType "DWord"
}
else{
Write-Host "`t`Creating $key"
New-ItemProperty -Path $key -Name "DisabledByDefault" -Value 0 -PropertyType "DWord"
}
}
}
}
SetProto -TargetKey $TargetKey -Action $Action
Write-Host "The operation completed successfully, reboot is required" -ForegroundColor Green
Use the Network monitoring tool to check which protocol site is using.
Microsoft Network Monitor
Do not forget to restart the machine after enabling or disabling the protocol.

How to go to any specific line in PowerShell during execution

if ((Get-WmiObject -ComputerName . -Class Win32_ComputerSystem).Domain -eq "domain.local") {
# Check with User if she/he wants to continue or not
Write-Host "Hurray you are in domain.local domain"
$FirstCondition = Read-Host -Prompt "Do you want to continue? If yes please press [Y] else press[N] to discontinue"
if ($FirstCondition = "Y") {
# Check if server is pinging or not
if (Test-Connection -ComputerName . -Count 1 -ErrorAction SilentlyContinue) {
$NetworkAdapter = Get-WmiObject -ComputerName . -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=$true |
Select-Object -Property [a-z]* -ExcludeProperty IPX*,WINS*
Test-Connection $net.DNSServerSearchOrder[0] -Count 1 -ErrorAction Stop
}
} else {
Goto Line:1
}
}
In the else {Goto Line:1} part I'm facing an issue, as during execution I need this command to run the first line if ((Get-WmiObject -ComputerName . -Class Win32_ComputerSystem).Domain -eq "domain.local") and check if the server is in domain.local domain or not. Please help me find a substitute for Goto in PowerShell 2.0.

Why does my VSTS custom task passes with exit code 1?

I've the following annoying problem. My custom VSTS build task won't fail.
It always passes, while the $LASTEXITCODE is other then zero.
The code does as expected. It generates an error in the log. Despite that, the step succeeds and the build / release continues.
Screenshot:
I've included a write-host with the exit code as well which shows exitcode 1 as well.
Code:
Try {
....
#Loop through the server list
Foreach ($Server in $machines)
{
# Use SSL or not
If($UseSSL -eq $true)
{
Write-Host "Connecting to $Server using a SSL connection (TCP/5986), Skip CA Check: $CheckCA ..."
$s = New-PSSession -ComputerName $Server -Credential $Cred -UseSSL -SessionOption $SessionOptions
}
Else
{
Write-Host "Connecting to $Server with an unsecure connection (TCP/5985) ..."
$s = New-PSSession -ComputerName $Server -Credential $Cred
}
# Run
$ExitCode = Invoke-Command -Session $s -ScriptBlock $script -ArgumentList $ApplicationPoolName,$Action,$Killswitch
# Cleanup the session
Write-Host "Closing connection to $Server."
Remove-PSSession -Session $s
}
} Catch {
Write-Host "##vso[task.logissue type=Error;]$Error"
$ExitCode = 1
} Finally {
#Leave TFS/VSTS trace
if (Get-Command -Name Trace-VstsEnteringInvocation -ErrorAction SilentlyContinue) {
Trace-VstsLeavingInvocation $MyInvocation
}
write-host "ExitCode: $ExitCode"
Exit $ExitCode
}
What am i missing here?
I solved it by removing the finally part.
Not working:
try {
.... do stuff ....
} catch {
write-error "some error"
exit 1
} Finally {
.. some final steps ...
}
Working:
try {
.... do stuff ....
} catch {
write-error "some error"
exit 1
}
For people arriving here, having the same problem with BAT/CMD files, my solution was to add this line:
exit /b %ERRORLEVEL%
to force transmitting the errorlevel to VSTS/TFS Command-Line Task

Powershell: search subfolders and run script on each subfolder that contains file1.txt and continue searching

I am looking for a bit of help on a script that :
Would search in sub folder and every time it would find a WIM file, it would update it with the latest updates from wsus. When that script is finished then it would continue with next subfolder.
Here you have the code i had so far:
$thismonth = (get-date).AddMonths(0).ToString("yyyMM")
$lastmonth = (get-date).AddMonths(-1).ToString("yyyMM")
$2ndlastmonth = (get-date).AddMonths(-2).ToString("yyyMM")
$date = get-date -f "yyyMM"
$LogName = "wim_update_$($thismonth).log"
$UpdatesPath = "d:\WSUS\WsusContent\*"
$MountPath = “d:\temp\mount”
$folder = Get-ChildItem -Path "D:\Distribution\Operating Systems"
########### DISM Mount path ########
Set-Location "d:\Distribution\Servicing\x86\"
forEach ($os in $folder)
{
###########Alias########
$WimFile = “d:\Distribution\Operating Systems\$os\sources\install.wim"
Write-Host $WimFile
if (-Not (Test-Path ($Wimfile))) { continue }
########### Backup WIM File########
Copy-Item "d:\Distribution\Operating Systems\$os\sources\install.wim" -Destination "d:\backup\install-$os-$thismonth.wim"
if (-Not $?)
{
Write-Host "Failed to copy WIM file"
continue
}
./DISM /Mount-Wim /WimFile:$WimFile /index:1 /Mountdir:$MountPath
if (-Not $?)
{
Write-Host "Failed to mount WIM file"
continue
}
############ Apply updates
$UpdateArray = Get-Item $UpdatesPath | where{$_.extension -eq ".cab"}
ForEach ($Updates in $UpdateArray)
{
./DISM /image:$MountPath /LogPath:d:\Temp\$LogName /LogLevel:3 /Format:List /Add-Package /Packagepath:$Updates
Start-Sleep –s 10
}
Write-Host "Updates Applied to WIM" -foregroundcolor red -backgroundcolor yellow
./DISM /Commit-Wim /Mountdir:$MountPath
########### DISM : Unmount WIM file ########
./DISM /Unmount-Wim /Mountdir:$MountPath /commit
./DISM /Cleanup-Wim
}
###########Remove N-2 ########
#Remove-Item "d:\backup\$os\install-$os-$2ndlastmonth.wim"

Resources