Setting app pools to recycle at multiple specific times via powershell or appcmd does not display both times in GUI/IIS - powershell

I am attempting to set app pools to recycle at multiple times in the day in iis 8.5, I've tried using powershell and app command and when testing on a server that has no sites/applications in the pool it seems to work perfectly, however when trying to set using either method on a server that has sites in the app pools I'm seeing strange behavior, It seems to work however in the GUI of IIS if i look at the recycling settings of the app pool it only shows one of the times specified.
Powershell script initially tried using is:
function Set-ApplicationPoolRecycleTimes {
param (
[string]$ApplicationPoolName,
[string[]]$RestartTimes
)
Import-Module WebAdministration
Write-Output "Updating recycle times for $ApplicationPoolName"
# Delete all existing recycle times
Clear-ItemProperty IIS:\AppPools\$ApplicationPoolName -Name Recycling.periodicRestart.schedule
Clear-ItemProperty IIS:\AppPools\$ApplicationPoolName -Name Recycling.periodicRestart.time
foreach ($restartTime in $RestartTimes) {
Write-Output "Adding recycle at $restartTime"
# Set the application pool to restart at the time we want
New-ItemProperty -Path "IIS:\AppPools\$ApplicationPoolName" -Name Recycling.periodicRestart.schedule -Value #{value=$restartTime}
Set-ItemProperty -Path "IIS:\AppPools\$ApplicationPoolName" -Name Recycling.periodicRestart.time -Value "00:00:00"
} # End foreach restarttime
} # End function Set-ApplicationPoolRecycleTimes
$apppoolname1 = "app pool's name"
$restartat = #("1:45", "18:45")
Set-ApplicationPoolRecycleTimes -ApplicationPoolName $apppoolname1 -RestartTimes $restartat
Again this seems to work perfectly unless there are sites in the application pool. When sites exist it seems to work except that the gui only shows one of the times set:
however querying the value show's both times:
Import-Module WebAdministration
(Get-ItemProperty ('IIS:\AppPools\app pool name') -Name Recycling.periodicRestart.schedule.collection) | select value
value
-----
18:45:00
01:45:00
also attempted using appcmd but finding the same results, works perfectly on a server with no sites in the app pool, but when run against servers with sites, missing one of the times in the gui, querying shows both times. I have turned logging on for app pool recycles to confirm it's happening at both times but wondering if I'm just overlooking something obvious.
appcmd script:
CD C:\windows\System32\inetsrv
$V1 = "app pool name"
#clears any existing schedule
cmd.exe /c appcmd.exe set apppool /apppool.name: $V1 /-recycling.periodicRestart.schedule
#setting desired recycles
cmd.exe /c appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='$v1'].recycling.periodicRestart.schedule.[value='01:45:00']" /commit:apphost
cmd.exe /c appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='$v1'].recycling.periodicRestart.schedule.[value='18:45:00']" /commit:apphost

I tried your PowerShell script with the application pool which contains a site or without site.in both the condition your posted script is working.
you could try to use the below script:
function Set-ApplicationPoolRecycleTimes {
param (
[string]$ApplicationPoolName,
[string[]]$RestartTimes
)
Import-Module WebAdministration
Write-Output "Updating recycle times for $ApplicationPoolName"
# Delete all existing recycle times
Clear-ItemProperty IIS:\AppPools\$ApplicationPoolName -Name Recycling.periodicRestart.schedule
foreach ($restartTime in $RestartTimes) {
Write-Output "Adding recycle at $restartTime"
# Set the application pool to restart at the time we want
New-ItemProperty -Path "IIS:\AppPools\$ApplicationPoolName" -Name Recycling.periodicRestart.schedule -Value #{value=$restartTime}
} # End foreach restarttime
} # End function Set-ApplicationPoolRecycleTimes
$apppoolname = "abcsite"
$restartat = #("05:55", "12:55", "17:00")
Set-ApplicationPoolRecycleTimes -ApplicationPoolName $apppoolname -RestartTimes $restartat
or
appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='test1'].recycling.periodicRestart.schedule.[value='07:00:00']" /commit:apphost
appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='test1'].recycling.periodicRestart.schedule.[value='18:25:00']" /commit:apphost
or
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.applicationHost/applicationPools/add[#name='test1']/recycling/periodicRestart/schedule" -name "." -value #{value='07:00:00'}
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.applicationHost/applicationPools/add[#name='test1']/recycling/periodicRestart/schedule" -name "." -value #{value='18:25:00'}
The above scripts are tested with the IIS 10 (Windows 10) and IIS 8.5(windows server 2012r2)

Related

How to prevent multiple instances of the same PowerShell 7 script?

Context
On a build server, a PowerShell 7 script script.ps1 will be started and will be running in the background in the remote computer.
What I want
A safenet to ensure that at most 1 instance of the script.ps1 script is running at once on the build server or remote computer, at all times.
What I tried:
I tried meddling with PowerShell 7 background jobs (by executing the script.ps1 as a job inside a wrapper script wrapper.ps1), however that didn't solve the problem as jobs do not carry over (and can't be accessed) in other PowerShell sessions.
What I tried looks like this:
# inside wrapper.ps1
$running_jobs = $(Get-Job -State Running) | Where-Object {$_.Name -eq "ImportantJob"}
if ($running_jobs.count -eq 0) {
Start-Job .\script.ps1 -Name "ImportantJob" -ArgumentList #($some_variables)
} else {
Write-Warning "Could not start new job; Existing job detected must be terminated beforehand."
}
To reiterate, the problem with that is that $running_jobs only returns the jobs running in the current session, so this code only limits one job per session, allowing for multiple instances to be ran if multiple sessions were mistakenly opened.
What I also tried:
I tried to look into Get-CimInstance:
$processes = Get-CimInstance -ClassName Win32_Process | Where-Object {$_.Name -eq "pwsh.exe"}
While this does return the current running PowerShell instances, these elements carry no information on the script that is being executed, as shown after I run:
foreach ($p in $processes) {
$p | Format-List *
}
I'm therefore lost and I feel like I'm missing something.
I appreciate any help or suggestions.
I like to define a config path in the $env:ProgramData location using a CompanyName\ProjectName scheme so I can put "per system" configuration.
You could use a similar scheme with a defined location to store a lock file created when the script run and deleted at the end of it (as suggested already within the comments).
Then, it is up to you to add additional checks if needed (What happen if the script exit prematurely while the lock is still present ?)
Example
# Define default path (Not user specific)
$ConfigLocation = "$Env:ProgramData\CompanyName\ProjectName"
# Create path if it does not exist
New-Item -ItemType Directory -Path $ConfigLocation -EA 0 | Out-Null
$LockFilePath = "$ConfigLocation\Instance.Lock"
$Locked = $null -ne (Get-Item -Path $LockFilePath -EA 0)
if ($Locked) {Exit}
# Lock
New-Item -Path $LockFilePath
# Do stuff
# Remove lock
Remove-Item -Path $LockFilePath
Alternatively, on Windows, you could also use a scheduled task without a schedule and with the setting "If the task is already running, then the following rule applies: Do not start a new instance". From there, instead of calling the original script, you call a proxy script that just launch the scheduled task.

How to get the proper PID of a newly created Firefox window in Powershell?

Here is a very simple example of the problem I am experiencing:
$process = Start-Process 'C:\Program Files\Mozilla Firefox\firefox.exe' -argumentlist "-new-window https://google.com -foreground" -PassThru
Write-Host $process.Id
The firefox window will start and work as expected, it will return a process id, but when I actually check the running processes, I see no results for that PID.
I tried adding this just to see,
while (-not $process.HasExited){
Write-Host "..."
}
Write-Host $process.HasExited
And it looks like the process does run for maybe a couple milliseconds before it exits.
I'm thinking this may have something to do with how Firefox handles it's own processes. Because I tested a similar setup with some other random apps and they all worked as expected.
Any ideas on how to work around this when it comes to Firefox?
There are several challenges to overcome:
The firefox process that ends up presenting the actual browser window is different from the one that is initially launched. That is, as you've observed, the launched process spawns other processes and itself exits quickly.
As Olaf points out, modern browsers typically launch multiple non-transient processes, so the challenge is how to identify the one that represent the browser window.
Browsers may reuse existing processes, so a single process can present multiple windows / tabs, and closing one of them won't terminate the process as a whole.
If you need to ensure that a dedicated, new process is used, you have two options:
(a) Make sure that no preexisting Firefox instance is running, either by erroring out, or - if acceptable for your use case - by forcefully terminating all existing instances first (Stop-Process -Name firefox).
(b) With significantly more effort, create a dedicated, temporary Firefox profile that you can launch with the -new-instance option, which allows multiple independent Firefox instances to run concurrently and whose lifetime can be tracked separately.
The following - cumbersome - solution implements option (b):
If no firefox process is found, there is no concern about creating independent instances, and Firefox can be launched normally.
Otherwise, a temporary profile is created, and launched via the -new-instance and -profile options to ensure that a new process will be used to present the new browser window.
After launching the initial process, loop until a firefox process appears that was launched later and has a nonempty window title, which is then presumed to be the real process of interest.
You can then wait for the termination of this process to know when the dedicated browser window has been closed. If a temporary profile had to be created, it is cleaned up afterwards.
# Comment this statement out to silence the verbose messages below.
$VerbosePreference = 'Continue'
$now = Get-Date
$url = 'https://example.org' # URL to open.
# Launch a (new) Firefox instance.
if ($alreadyRunning = [bool] (Get-Process -ErrorAction Ignore firefox)) {
# Determine the path for a temporary profile with a unique name.
$tempProfilePath = Join-Path ([IO.Path]::GetTempPath()) ([datetime]::utcnow.tostring('o') -replace '\D')
Write-Verbose "Firefox is already running. Creating temp. profile $tempProfilePath..."
# Note: Creating an empty directory for the profile is seemingly enough.
$null = New-Item -Type Directory $tempProfilePath
Write-Verbose "and starting a new instance with it..."
Start-Process firefox "-new-instance -profile $tempProfilePath $url"
} else {
Write-Verbose 'Firefox isn''t running. Starting normally...'
Start-Process firefox $url
}
# Find the newly launched process that is the actual browser window.
Write-Verbose 'Waiting for a recently launched Firefox process with a nonempty window title to appear...'
while (-not (
$ps = Get-Process firefox |
Where-Object StartTime -gt $now |
Where-Object MainWindowTitle
)) {
Write-Host -NoNewLine .
Start-Sleep -MilliSeconds 500
}
Write-Host
Write-Verbose "Found. Waiting for process to exit..."
$ps.WaitForExit()
Write-Verbose 'Process has exited.'
if ($alreadyRunning) {
Write-Verbose "Cleaning up temporary profile $tempProfilePath..."
do {
# The profile dir. is typically held on to for a little while longer by associated processes that may not have terminated yet.
Start-Sleep -MilliSeconds 200
Remove-Item -ErrorAction Ignore -Literalpath $tempProfilePath -Recurse -Force
}
while (Test-Path -LiteralPath $tempProfilePath)
}
Thanks to #mklement0 work, in your case you can use the parent Process ID.
I use WMI to get the parent process, but it works for the very first launch.
$parentProcess = Start-Process 'C:\Program Files\Mozilla Firefox\firefox.exe' -argumentlist "-new-window https://google.com -foreground" -PassThru
$childProcess = get-process -id $(Get-CimInstance -Class Win32_Process -Filter "Name = 'firefox.exe'" | where {$_.ParentProcessId -eq $parentProcess.id}).ProcessId
# effectively stop the child
$childProcess | Stop-Process

New-WebApplication fails using network path in physicalpath parameter in powershell

We are trying to create list of applications in a particular site,
For this we have a csv which contains required information for site creation. Below is the format:
Below is the script which we are using to create application in IIS:
foreach($app in $apps){
$appname = $app.path.TrimStart("/")
New-WebApplication -Name $appname -Site $app.Site -PhysicalPath $app.PhysicalPath -ApplicationPool $app.applicationPool -Verbose
}
Problem is application whose code is stored on some network path like \\network\Webapps\appname are giving below error:
New-WebApplication : A parameter cannot be found that matches parameter name 'physicalPath'.
whereas application folder located on same server are being created without any issue,
Have also done test-path \\network\webapps\appname it results in true
What is the issue and how to rectify it?
Issue got resolved, I didn't got what was the exact reason behind that, Have updated my script, basically have assigned values to specific variable than was able to create application.
Updated Script
foreach($app in $apps){
$appname = $app.path.TrimStart("/")
$appSite = $app.Site
$appPath = $app.PhysicalPath
$appPool = $app.applicationPool
New-WebApplication -Name $appname -Site $appSite -PhysicalPath $appPath -ApplicationPool $appPool -Verbose
}
Thanks
First of all, you have to make sure that powershell has permission to access the server corresponding to the network path when creating the site.
When using powershell to create a site, if the physical path of the site is a file in the local disk, you can use New-WebApplication, if it is a UNC path, you should use New-Item.
Under normal circumstances, the UNC path will not be used as the physical path of the main site, but will be added as a virtual directory. Because the domain name of the main site points to the local machine and the UNC points to the remote IP address, the two will conflict because a server error occurred when I created the site through IIS Manager.
$siteName = 'Default Web Site'
$virtualDirectoryName = 'Test'
$physicalPath = '\\UNC-path'
## Init
$virtualDirectoryPath = "IIS:\Sites\$siteName\$virtualDirectoryName"
## Create Virtual Directory where physicalpath is an UNC-path (New-WebVirtualDirectory
wont do)
New-Item $virtualDirectoryPath -type VirtualDirectory -physicalPath $physicalPath
Any chance it's this? That error sounds like it's an issue with the cmdlet and not the data you're feeding it.
https://social.msdn.microsoft.com/Forums/en-US/3e0cb076-6ad4-4b03-bb59-c912174266b0/container-with-iis-cant-create-application-using-powershell-physicalpath-parameter-not-found?forum=windowscontainers
specifically:
You need to import the WebAdministration module. If you're in the container through docker exec simply use:
Import-Module WebAdministration

Resume powershell script after reboot [duplicate]

This question already has answers here:
Powershell - Reboot and Continue Script
(7 answers)
Closed 5 years ago.
I have a lot of articles about this subject but none of it is understandable.
My request is very simple. I have two part of my codes ; First of all Code 1 should be work and windows should be restarted. After reboot completion, Code 2 should be work. This process should be done silently at background. Powershell version is 4.0 ( Win 2012 R2 )
CODE 1 - This code is changing Computer Primary DNS Suffix.
$computerName = $env:computername
$DNSSuffix = "abc.com"
$oldDNSSuffix = (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\" -Name "NV Domain")."NV Domain"
#Update primary DNS Suffix for FQDN
Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\" -Name Domain -Value $DNSSuffix
Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\" -Name "NV Domain" -Value $DNSSuffix
#Update DNS Suffix Search List - Win8/2012 and above - if needed
#Set-DnsClientGlobalSetting -SuffixSearchList $oldDNSSuffix,$DNSSuffix
#Update AD's SPN records for machine if part of an AD domain
if ((gwmi win32_computersystem).partofdomain -eq $true) {
$searchAD = new-object System.DirectoryServices.DirectorySearcher
$searchAD.filter = "(&(objectCategory=computer)(cn=$($computerName)))"
$searchADItem = $searchAD.FindAll() | select -first 1
$adObj= [ADSI] $searchADItem.Path
$oldadObjSPN = $searchADItem.Properties.serviceprincipalname
$adObj.Put('serviceprincipalname',($oldadObjSPN -replace $oldDNSSuffix, $DNSSuffix))
$oldadObjDNS = $searchADItem.Properties.dnsHostName
$adObj.Put('dnsHostName',($oldadObjDNS -replace $oldDNSSuffix, $DNSSuffix))
$adObj.setinfo()
#$adObj.Get('serviceprincipalname')
#$adObj.Get('dnsHostName')
}
CODE 2 - Installing Terminal Services on this computer
Import-Module RemoteDesktop
Add-WindowsFeature -Name RDS-RD-Server -IncludeAllSubFeature
Add-WindowsFeature -Name RDS-Licensing -IncludeAllSubFeature
If you don’t want to roll your own with log files to check where it left off, you can look into powershell workflows which allows recovery after reboot. See https://technet.microsoft.com/en-us/library/jj574130(v=ws.11).aspx
powershell commands are very complicated, i decided to do this with batch file.
Resume batch script after computer restart

Loading ntuser.dat with powershell

I need to check some settings for all users on Windows clients in the network. All users have roaming profiles.
I have written a Powershell script that loads an offline copy of a users' NTuser.dat and reads out the specific keys. Then the file is unloaded and the next one is loaded into the registry.
The problem is that after about 10 users no new files are loaded. When the script is launched again the users still don't load. New users are only after I close the Powershell prompt and open a new one. The script always stalls after about 10 users.
$userlist = ls "C:\Temp calls\profiles"
foreach ($user in $userlist){
$username = $user.name
#$username = "ciproda"
reg load "hklm\$username" "C:\Temp calls\profiles\$username\NTUSER.DAT" | Out-Null
...
Here I check the keys
...
[gc]::collect()
start-sleep -s 3
reg unload "hklm\$username"
}
In your section 'Here I check the keys', are you mounting the hive as a PS drive using something like:
new-Psdrive -name <blah> -PSProvider Registry -root <blih>
cd <blah>:
# Some Set-ItemProperty and Get-ItemProperty calls here referring to
# your PSDrive and using PowerShell variables
Remove-PSDrive <blah>
If you still have references to some of your PSDrive variables before calling REG UNLOAD, that call might fail. Try to remove all variables that would still refer to your PSDrive through Remove-Variable.