Set App Pool recycle time with PowerShell trouble - powershell

I want to set two specific times to recycle my App Pools in IIS.
In the GUI, if you want to set multiple specific times you enter it like so 00:15, 04:30.
Below is my PowerShell. It only sets the first time - 00:15. Unfortunately, no error is happening, and so there is no error to post.
Why is this only setting the first time? I am using New-ItemProperty over Set-ItemProperty.
Import-Module WebAdministration
$RecycleTimes = #("00:15", "04:30")
$PrivateMemory = 1468006    
$sites = Get-ChildItem 'IIS:\AppPools\' <# | Where-Object { $_.recycling.periodicRestart.schedule.TotalMilliseconds -gt 1 } #>
    foreach ($s in $sites) {
        # Turn off Fixed Intervals recylcing setting
        Set-ItemProperty $s.PSPath -Name Recycling.periodicRestart.time -Value 0.00:00:00
        # Set application pool recycle time
        Clear-ItemProperty $s.PSPath -Name Recycling.periodicRestart.schedule
        foreach ($RecycleTime in $RecycleTimes) {
            New-ItemProperty $s.PSPath -Name Recycling.periodicRestart.schedule -Value #{value=$RecycleTime}
        }
        #Set Private memory usage
        Set-ItemProperty $s.PSPath -Name recycling.periodicrestart.privateMemory -Value $PrivateMemory
    }

I used the powershell command line code you provided on my local machine and can successfully set two recycle times. This means there is nothing wrong with your code.
You can try to use the following basic powershell command line code to set multiple specific recycling application pool time for the default website, see if it works.
Import-Module WebAdministration
Set-ItemProperty -Path "IIS:\AppPools\DefaultAppPool" -Name Recycling.periodicRestart.schedule -Value #{value="00:15"}
New-ItemProperty -Path "IIS:\AppPools\DefaultAppPool" -Name Recycling.periodicRestart.schedule -Value #{value="04:30"}

Related

Add logging quickly to an existing set of powershell scripts

I've got a collection of powershell scripts, some of which call others. Some of these subscripts can also be called on their own as needed. How can I quickly add logging to all of the scripts so that any script invocation results in a log file for later examination?
There are a number of questions dealing with logging with some great answers, like this one. But I wanted to see what we could come up with that:
required minimal touching of the existing powershell files
automatically dealt with script A.ps1 calling script B.ps1. If you call
A.ps1, A.ps1 needs to start and finish the logging. But if you call B.ps1
directly, B.ps1 does.
I came up with my answer below, and wanted to share and see if there were other ideas on how to approach this, or suggestions for improvement on my answer.
The support code I write (further down) allows for just adding the following to each ps1 file. It automatically gives me logging regardless of if a script is called at top-level or by another script:
#any params for script
. "$PSScriptRoot\ps_support.ps1"
StartTranscriptIfAppropriate
try
{
#all of the original script
}
finally
{
ConditionalStopTranscript
}
The code that powers this is in ps_support.ps1, sitting next to my collection of powershell files that need logging. It uses Get-Variable and Set-Variable to manipulate a couple variables at the caller's scope level:
Logging__TranscriptStarted is normal so sub-scopes can see that
logging is already happening and not try to start it again.
Logging__TranscriptStartedPrivate is private so a scope can know if
it is responsible for stopping the logging.
Here is ps_support.ps1:
Set-Variable -name TranscriptStartedPropertyName -opt ReadOnly -value 'Logging__TranscriptStarted'
Set-Variable -name TranscriptStartedPrivatePropertyName -opt ReadOnly -value 'Logging__TranscriptStartedPrivate'
function StartTranscriptIfAppropriate
{
$transcriptStarted = [bool](Get-Variable -name $TranscriptStartedPropertyName -ErrorAction Ignore)
if (-not $transcriptStarted)
{
$callstack = get-pscallstack
$fullPath = $callstack[$callstack.count-2].ScriptName
$name = Split-Path -Path $fullPath -Leaf
$directory = Split-Path -Path $fullPath
$logDirectory = [IO.Path]::GetFullPath("$directory\..\scripts_logs")
md -force $logDirectory | out-null
$logFinalPath = "$logDirectory\$(Get-Date -Format o | foreach {$_ -replace ":", "."})_$name.log"
Set-Variable -scope 1 -name $TranscriptStartedPropertyName -value $True
Set-Variable -scope 1 -option private -name $TranscriptStartedPrivatePropertyName -value $True
Start-Transcript $logFinalPath | Write-Host
}
$immediateCallerPath = Get-Variable -scope 1 -name PSCommandPath -ValueOnly
Write-Host "Starting script at $immediateCallerPath"
}
function ConditionalStopTranscript
{
$immediateCallerPath = Get-Variable -scope 1 -name PSCommandPath -ValueOnly
Write-Host "Stopping script at $immediateCallerPath"
$transcriptStartedByMe = [bool](Get-Variable -scope 1 -name $TranscriptStartedPrivatePropertyName -ErrorAction Ignore)
if ($transcriptStartedByMe)
{
Stop-Transcript | Write-Host
}
}

Powershell MSUtil.LogQuery not closing SQL connection

I've written a PowerShell scrip that iterates through a large number of IIS W3C logfiles and inserts the values into a MSSQL database.
Set-Variable -Name "UnprocessedDir" -Value "X:\files" -Description "Folder for unprocessed log files" Scope Script
Set-Variable -Name "InputObject" -Value (New-Object -comObject MSUtil.LogQuery.IISW3CInputFormat) -Description "Log Parser input COM object" -Scope Script
Set-Variable -Name "OutputObject" -Value (New-Object -comObject MSUtil.LogQuery.SQLOutputFormat) -Description "Log Parser output COM object" -Scope Script
$OutputObject.clearTable = $false
$OutputObject.createTable = $false
$OutputObject.database = "Database_Name"
$OutputObject.driver = "SQL Server"
$OutputObject.dsn = "DSN_Name"
$OutputObject.fixColNames = $true
$OutputObject.ignoreIdCols = $true
$OutputObject.ignoreMinWarns = $true
$OutputObject.maxStrFieldLen = 511
$OutputObject.oConnString = $null
$OutputObject.password = $null
$OutputObject.server = "sqlserver.domain.com\INSTANCENAME"
$OutputObject.transactionRowCount = 5000
$OutputObject.username = $null
Set-Variable -Name "IISLogs" -Value #(Get-ChildItem -Path $UnprocessedDir -Recurse -File) -Description "Array of files to be imported into SQL" -Scope Script
Set-Variable -Name "LPComObj" -Value (New-Object -com MSUtil.LogQuery) -Description "COM Object used to import Log Parser records into MSSQL" -Scope Script
Write-Output "$(Get-ISOTimeStamp) Beginning SQL import. $($IISLogs.Count) Files to be imported"
$IISLogs | ForEach-Object { $loop = 0 } {
Set-Variable -Name "SubDir" -Value $(($_.FullName).Split('\')[-2]) -Description "Subdirectory where log file is located" -Scope Script
Set-Variable -Name "LogType" -Value $(($_.FullName).Split('\')[-3]) -Description "Type of log being imported" -Scope Script
Set-Variable -Name "ServerName" -Value $(($_.FullName).Split('\')[-4]) -Description "ServerName of file being imported" -Scope Script
Set-Variable -Name "LPQuery" -Description "Query to use in Log Parser" -Scope Script -Value #"
SELECT
-- FIELDS LogFilename,LogRow,date,time,c-ip,cs-username,s-sitename,s-computername,s-ip,s-port,cs-method,cs-uri-stem,cs-uri-query,sc-status,sc-substatus,sc-win32-status,sc-bytes,cs-bytes,time-taken,cs-version,cs-host,cs(User-Agent),cs(Cookie),cs(Referer),s-event,s-process-type,s-user-time,s-kernel-time,s-page-faults,s-total-procs,s-active-procs,s-stopped-procs
-- STANDARD FIELDS date,time,s-ip,cs-method,cs-uri-stem,cs-uri-query,s-port,cs-username,c-ip,cs(User-Agent),cs(Referer),sc-status,sc-substatus,sc-win32-status,time-taken
'$($ServerName)' as [servername],
'$($_.Name)' as [filename],
LogRow AS [row],
'$($LogType)' as [logtype],
TO_TIMESTAMP(date,time) AS [timestamp],
[s-ip],
[cs-method],
[cs-uri-stem],
[cs-uri-query],
TO_INT([s-port]),
[cs-username],
[c-ip],
[cs(User-Agent)],
[cs(Referer)],
TO_INT([sc-status]),
TO_INT([sc-substatus]),
TO_INT([sc-win32-status]),
TO_INT([time-taken]),
0 AS lock
INTO IIS_W3C
FROM '$($_.FullName)'
"#
Set-Variable -Name "LPResult" -Value ($LPComObj.ExecuteBatch($LPQuery, $InputObject, $OutputObject)) -Description "IIS Log File imported into SQL" -Scope Script
If ($LPResult -eq $false)
{
Write-Output "$(Get-ISOTimeStamp) Data imported from `"$($_.FullName)`""
Set-Variable -Name "loop" -Value ($loop + 1) -Description "Increase loop iteration Count" -Scope Script
}
Else
{
Write-Output "$(Get-ISOTimeStamp) Log Parser returned errors importing `"$($_.FullName)`""
Throw "$(Get-ISOTimeStamp) Log Parser returned errors importing `"$($_.FullName)`""
}
}
The number of logs I'm importing is tens of thousands; the code above works spectacularly for a few hundred files, but after a few hours, it crashes. From what I can tell, it looks like every iteration of the ForEach-Object loop creates a new SQL TCP connection which is not terminated at the end of the loop.
I've tried creating the $LPComObj both within and outside of the loop. I've tried Remove-Variable. I've tried some generic commands like $LPComObj.Close(), .Remove(), .Quit(), etc. The MSUtil.LogQuery method itself does not seem to contain any methods to close the SQL TCP connection, and as the script is running, I can see more and more TCP connections piling up. I tried a few things using [System.Runtime.Interopservices.Marshal]:: to release/remove the COM object, but none of them closed the TCP connection. Even closing the PowerShell session doesn't kill the connection.
The only way I was able to do this is by hunting down and finding the dllhost.exe" process that was using the ports and killing it. But from within the script, there isn't a clean way to get the PID of the offending dllhost.exe process. (Trying to kludge some variant of Get-Process | Stop-Process might work, but would add a lot of time to the execution of the script.)
What other ways might I be able to work around this problem?

Enable Windows 10 Developer Mode programmatically

I know you can enable Windows 10 Developer mode interactively by going to Settings | For developers, selecting 'Developer mode' and then rebooting.
Is there a way to enable this programmatically? (eg. via PowerShell or similar so that I can include it as a step in a Boxstarter script when refreshing my developer workstation)
Turns out Nickolaj Andersen has written an article which includes just such a PowerShell script..
http://www.scconfigmgr.com/2016/09/11/enable-ubuntu-in-windows-10-during-osd-with-configmgr/
Here are the relevant lines extracted from his post:
# Create AppModelUnlock if it doesn't exist, required for enabling Developer Mode
$RegistryKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
if (-not(Test-Path -Path $RegistryKeyPath)) {
New-Item -Path $RegistryKeyPath -ItemType Directory -Force
}
# Add registry value to enable Developer Mode
New-ItemProperty -Path $RegistryKeyPath -Name AllowDevelopmentWithoutDevLicense -PropertyType DWORD -Value 1
I modified the accepted answer and ended up with the following script:
param([Switch]$WaitForKey)
if (([Version](Get-CimInstance Win32_OperatingSystem).version).Major -lt 10)
{
Write-Host -ForegroundColor Red "The DeveloperMode is only supported on Windows 10"
exit 1
}
# Get the ID and security principal of the current user account
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
# Get the security principal for the Administrator role
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator
if ($myWindowsPrincipal.IsInRole($adminRole))
{
$RegistryKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
if (! (Test-Path -Path $RegistryKeyPath))
{
New-Item -Path $RegistryKeyPath -ItemType Directory -Force
}
if (! (Get-ItemProperty -Path $RegistryKeyPath -Name AllowDevelopmentWithoutDevLicense))
{
# Add registry value to enable Developer Mode
New-ItemProperty -Path $RegistryKeyPath -Name AllowDevelopmentWithoutDevLicense -PropertyType DWORD -Value 1
}
$feature = Get-WindowsOptionalFeature -FeatureName Microsoft-Windows-Subsystem-Linux -Online
if ($feature -and ($feature.State -eq "Disabled"))
{
Enable-WindowsOptionalFeature -FeatureName Microsoft-Windows-Subsystem-Linux -Online -All -LimitAccess -NoRestart
}
if ($WaitForKey)
{
Write-Host -NoNewLine "Press any key to continue..."
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
}
else
{
# We are not running "as Administrator" - so relaunch as administrator
# Create a new process object that starts PowerShell
$newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell";
# Specify the current script path and name as a parameter
$newProcess.Arguments = "-NoProfile",$myInvocation.MyCommand.Definition,"-WaitForKey";
# Indicate that the process should be elevated
$newProcess.Verb = "runas";
# Start the new process
[System.Diagnostics.Process]::Start($newProcess);
# Exit from the current, unelevated, process
exit
}
It automatically elevates itself, if not already running elevated and enables the optional feature mentioned by Julian Knight.

Powershell issues with app pool restart schedule

This is the first thing I have ever done with Powershell. The gist of the script is to do some base setup for my companies web server installation. Basically it should
Take a list of names
Make a user with that name
configure user make a folder with that name
make an application pool with that name
set application pool properties
set folder permissions for a group
The code is below.
Import-Module WebAdministration
#$apps = #('H','N1','N2')
foreach ($app in $apps){
## I need to check for if a user exists first
NET USER $app "password" /ADD
NET LOCALGROUP "iis_iusrs" $app /ADD
WMIC USERACCOUNT WHERE "Name=$app" SET PaswordExpires=False
#See if the application's folder exists
if (!(Test-Path("C:\inetpub\wwwroot\"+$app)))
{
New-Item -ItemType Directory -Force -Path C:\inetpub\wwwroot\$app
}
## See if the application Pool exists. If it doesn't then create it
if (!(Test-Path("IIS:\AppPools\"+$app)))
{
$appPool = New-Item ("IIS:\AppPools\"+$app)
$appPool.ProcessModel.identityType = 3
$appPool.ProcessModel.userName=$app
$appPool.ProcessModel.password="password"
$appPool.ProcessModel.idleTimeout=[TimeSpan]::FromMinutes(0)
$appPool.processModel.loadUserProfile="True"
$appPool.recycling.disallowOverlappingRotation = "True"
$appPool.recycling.disallowRotationOnConfigChange = "True"
$appPool.recycling.periodicRestart.time = [TimeSpan]::FromMinutes(0)
$appPool | Set-Item
$propPath = "IIS:\AppPools\"+$appPool
New-ItemProperty -Path $propPath -Name recycling.periodicRestart.schedule -Value #{value="00:06:00"}
}
}
$webserver = Hostname
if(!(Test-Path("C:\"+$webserver)))
{
$hostserver = Read-Host "What is the host server's name?"
New-Item -ItemType Directory -Force -Path C:\$webserver\$hostserver
}
if(!(Test-Path('C:\Temp')))
{
New-Item -ItemType Directory -Force -Path C:\Temp
}
$Acl_inetpub = Get-Acl C:\inetpub
$Acl_temp = Get-Acl C:\Temp
$Acl_windows_temp = Get-Acl C:\Windows\Temp
$Ar_iis = New-Object System.Security.AccessControl.FileSystemAccessRule(".\iis_iusrs","FullControl","ContainerInherit,ObjectInherit","None","Allow")
$Ar_users = New-Object System.Security.AccessControl.FileSystemAccessRule(".\users","FullControl","ContainerInherit,ObjectInherit","None","Allow")
$Acl_inetpub.SetAccessRule($Ar_iis)
$Acl_inetpub.SetAccessRule($Ar_users)
$Acl_temp.SetAccessRule($Ar_iis)
$Acl_temp.SetAccessRule($Ar_users)
$Acl_windows_temp.SetAccessRule($Ar_iis)
$Acl_windows_temp.SetAccessRule($Ar_users)
Set-Acl C:\inetpub $Acl_inetpub
Set-Acl C:\temp $Acl_temp
Set-Acl C:\Windows\Temp $Acl_windows_temp
My issue currently is.
$propPath = "IIS:\AppPools\"+$appPool
New-ItemProperty -Path $propPath -Name recycling.periodicRestart.schedule -Value #{value="00:06:00"}
I get a path error when I run this but I am not sure where the error is coming from. Also, is there a way to do this same thing but just using the object like I did the rest of the settings? So far I have not found a way to do this.
So I found the final issue thanks Randy Schuman's advice. I wasn't thinking about how I was using $app vs $appPool . I was just continuing the pattern of using the $appPool's object when I need to supply it the $appPools name which is just $app. When I switched that it worked.

How to add a site to 'Local intranet' zone in internet options using a PowerShell script?

I would like to add \XX01234ABC01 to Local intranet zone using a PowerShell script and possibly take the site from the variable below.
$computername=$env:computername -replace ".....$","ABC01"
Any help would be much appreciated.
I hope this help you
$prefixIntranet = "www"
$LocalIntranetSite = "xxxx.com"
$UserRegPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains"
$DWord = 1
try {
Write-Verbose "Creating a new key '$LocalIntranetSite' under $UserRegPath."
New-Item -Path "$UserRegPath" -ItemType File -Name "$LocalIntranetSite"
New-Item -Path "$UserRegPath\$LocalIntranetSite" -ItemType File -Name "$prefixIntranet"
Write-Verbose "Creating a Dword value named 'HTTP' and set the value to '$DWord' "
Set-ItemProperty -Path $UserRegPath\$LocalIntranetSite\$prefixIntranet -Name "http" -Value $DWord `
Write-Host "Successfully added '$prefixIntranet.$LocalIntranetSite' domain to Internet Explorer local intranet."
} Catch [System.Exception] {
Write-Warning "[CATCH] Errors found during attempt:`n$_" -BackgroundColor Red
}
I was looking to do something similar. This will do the job:
# Gets you PC Name like you had in your question
$LocalIntranetSite = $env:computername -replace ".....$","ABC01"
# Set your base registry location
$UserRegPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet
Settings\ZoneMap\Domains"
# Set your zone. 1 is intranet.
$DWord = 1
try {
Write-Debug "Creating a new key '$LocalIntranetSite' under $UserRegPath."
# Check to be sure the key isn't already listed before trying to add
if (-not (Test-Path -Path "$UserRegPath\$LocalIntranetSite")) {
# Add your site to the domains list in the registry
New-Item -Path "$UserRegPath" -ItemType File -Name "$LocalIntranetSite"
}
# Set a Dword property named '*' and with value '$DWord' on your '$LocalIntranetSite'.
# This is what adds your site to the Intranet Zone.
Set-ItemProperty -Path "$UserRegPath\$LocalIntranetSite" -Name "*" -Value $DWord
} Catch [System.Exception] {
Write-Warning "[CATCH] Errors found during attempt:`n$_"
}