I am developing a PowerShell script to modify customHeaders in my website's web.config file, specifically the Content-Security-Policy header. I'm doing this with a script for 2 reasons:
I am using the IISAdministration module to do this, first by grabbing the existing values for Content-Security-Policy and modifying them within a variable internal to the script. Then, I attempt to remove the existing Content-Security-Policy header, then re-add it with my modified values. Please note that this script runs after the web service has been initially deployed, so it's modifying a site that already exists.
My script looks like this:
Import-Module IISAdministration
# got $env (the environment the script is being run in) earlier
if ($env -eq "dev) {
$connectSrc = "; connect-src 'self' https://mydevurl.com"
} elseif ($env -eq "prod") {
$connectSrc = "; connect-src 'self' https://myprodurl.com"
} else {
$connectSrc = "; connect-src 'self' https://myuaturl.com"
}
$sectionPath = "system.webServer/httpProtocol"
$commitPath = "virtual_path_to_web_service"
$IISConfigSection = Get-IISConfigSection -SectionPath $sectionPath -CommitPath $commitPath
$IISConfigCollection = Get-IISConfigCollection -ConfigElement $IISConfigSection -CollectionName "customHeaders";
$Header = Get-IISConfigCollectionElement -ConfigCollection $IISConfigCollection -ConfigAttribute @{"name" = "Content-Security-Policy"}
if (!$Header) {
Write-Error "No Content-Security-Policy currently exists."
} else {
$existingPolicy = $Header.Attributes.value[1] # successfully grabs the current value of the Content-Security-Policy configured in IIS
$newPolicy = $existingPolicy + $connectSrc
Remove-IISConfigCollectionElement -ConfigCollection $IISConfigCollection -ConfigAttribute @{"name" = "Content-Security-Policy"}
New-IISConfigCollectionElement -ConfigCollection $IISConfigCollection -ConfigAttribute @{"name"= "Content-Security-Policy"; "value" = $newPolicy;};
$IIS = Get-IISServerManager
$IIS.CommitChanges();
}
The script runs fine until we get to Remove-IISConfigCollectionElement. I see a print statement like this in my console:
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):
But immediately it defaults to "Y", and then I get the following NullReferenceException:
Remove-IISConfigCollectionElement : Object reference not set to an instance of an object.
At path\to\my\script.ps1:50 char:1
+ Remove-IISConfigCollectionElement -ConfigCollection $IISConfigCollec ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Remove-IISConfigCollectionElement], NullReferenceException
+ FullyQualifiedErrorId : System.NullReferenceException,Microsoft.IIS.Powershell.Commands.RemoveIISConfigCollectio
nElementCommand
I also captured a more detailed stack trace in a different test:
Exception : System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.PowerShell.ConsoleHostUserInterface.PromptForChoice(String caption, String
message, Collection`1 choices, Int32 defaultChoice)
at
System.Management.Automation.Internal.Host.InternalHostUserInterface.PromptForChoice(String
caption, String message, Collection`1 choices, Int32 defaultChoice)
at System.Management.Automation.MshCommandRuntime.InquireHelper(String inquireMessage,
String inquireCaption, Boolean allowYesToAll, Boolean allowNoToAll, Boolean replaceNoWithHalt,
Boolean hasSecurityImpact)
at System.Management.Automation.MshCommandRuntime.DoShouldProcess(String
verboseDescription, String verboseWarning, String caption, ShouldProcessReason&
shouldProcessReason)
at System.Management.Automation.MshCommandRuntime.ShouldProcess(String target)
at System.Management.Automation.Cmdlet.ShouldProcess(String target)
at Microsoft.IIS.Powershell.Commands.RemoveIISConfigCollectionElementCommand.ProcessRecord()
at System.Management.Automation.CommandProcessor.ProcessRecord()
TargetObject :
CategoryInfo : NotSpecified: (:) [Remove-IISConfigCollectionElement], NullReferenceException
FullyQualifiedErrorId : System.NullReferenceException,Microsoft.IIS.Powershell.Commands.RemoveIISConfigCollectionElemen
tCommand
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at <ScriptBlock>, path\to\my\script.ps1:
line 45
at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo : {}
PSMessageDetails :
I have also tested Remove-IISConfigCollectionElement by omitting the ConfigAttribute parameter, like this:
Remove-IISConfigCollectionElement -ConfigCollection $IISConfigCollection
This results in the same error, so I think the error must be referring to $IISConfigCollection. But this doesn't make sense to me, because Get-IISConfigCollectionElement worked just fine a few lines earlier using $IISConfigCollection, and I was able to extract a value from it.
What is causing this error? Am I handling something wrong, or could there be an underlying bug with IIS? My IIS version is 10.0.17763.1. The only thing I can see from the stack trace is that there might be an error at Microsoft.PowerShell.ConsoleHostUserInterface.PromptForChoice, but to be honest, I don't even know why it's prompting, because I didn't include the -Confirm parameter when running Remove-IISConfigCollectionElement
The -Confirm
parameter is prompting for confirmation per the ConfirmImpact
value. More information can be found here.
If you add -Confirm:$false
to Remove-IISConfigCollectionElement
that should suppress the prompt and allow the command to complete.