I am debugging PowerShell DSC resources that come with v4.0.
More specifically, I am testing MSFT_ProcessResource by adding a diagnostic log.
After I make change to the resource, and run my configuration that exercise the resource, I don't see the logging I just added. Eventually after several minutes, PowerShell seems to refresh whatever cache of resource it has.
I've tried
Get-DscResource, and
Import-Module MSFT_ProcessResource
None of which worked.
Is there a way to force re-loading the resource?
DSC engine caches resources to increase performance.
There are two ways to reload the resource:
1) Restart process hosting DSC engine (kill WMI Provider Host and re-run the configuration)
2) Use debug mode which will cause DSC to reload resources automatically (useful when developing resources, but not recommended for regular work):
LocalConfigurationManager
{
DebugMode = $true
}
You can read more about debug mode here:
http://blogs.msdn.com/b/powershell/archive/2014/04/22/debug-mode-in-desired-state-configuration.aspx
DSC has a caching model which frankly seems buggy and poorlyl designed as of Sep 2016
The blog entries indicating the mechanisms to get around the caching don't always work
In your configuration include the following configuration line
Also perform a full restart of the winmgt service. Simply killing the dsctimer process doesn't appear to always work.
{
LocalConfigurationManager
{
DebugMode = "All"
}
}
A PowerShell script to clear the cache is:
$dscProcessID = Get-WmiObject msft_providers |
Where-Object {$_.provider -like 'dsctimer'} |
Select-Object -ExpandProperty HostProcessIdentifier
if ($dscProcessID -eq $null) {
Write-Host "DSC timer is not running."
return
}
Write-Host "Process ID: $dscProcessID"
Get-Process -Id $dscProcessID | Stop-Process -Force
Restart-Service -Name winmgmt -Force -Verbose
This has now changed with WMF 5, instead of $true debug mode has the following options.
None - Signifies that DebugMode is False and not applicable.
ForceModuleImport - Enforce the resource module to be reloaded instead of using the cache. This is similar to "true" value in
previous versions.
ResourceScriptBrealAll - Helps in debugging DSC resources when Local configuration manager tries to execute their functions. More on
it in subsequent blog posts!
All - Signifies that debugging as well as reloading of modules are both enabled.
Using this in an example DSC config would look like this:
Configuration myChocoConfig2
{
Import-DscResource -Module cChoco
Node "localhost"
{
LocalConfigurationManager
{
DebugMode = 'All'
}
cChocoInstaller installChoco
{
InstallDir = "c:\choco"
}
cChocoPackageInstaller installChrome
{
Name = "sysinternals"
DependsOn = "[cChocoInstaller]installChoco"
}
}
}
https://techstronghold.com/blogs/scripting/how-to-setup-debug-mode-in-windows-powershell-desired-state-configuration-dsc
I have a set of scripts that load on start of PowerShell and I often needed the same. I would edit one of my scripts and need it to be updated in the current session.
Because I have these scripts loading via a series of scripts in the $profile I am able to use one command to refresh for any of the scripts that I load on init.
C:> powershell
This command will refresh your session and keep you in the same folder you are currently in. If you are not loading your module on startup, you will need to use the answer from Karol.
Related
In a DSC configuration script for IIS, I am trying to remove the defaultPath lock from the httpErrors section but the way in which the feature delegation works does not apply to this section. Hence to do the following:
appcmd set config /section:httpErrors /lockAttributes:
I've tried using the xWebConfigProperty as follows:
xWebConfigProperty httpErrors_lockAttributes
{
WebsitePath = "MACHINE/WEBROOT/APPHOST"
Filter = "system.webServer/httpErrors"
PropertyName = "lockAttributes"
Value = ""
Ensure = "Absent"
}
However this fails with an error saying the lockAttributes attributes does not exist. And yet it is definitely in the ApplicationHost.config
My only remaining workaround is to run the appcmd as Script in the DSC (a little ugly). Any ideas?
You could use below PowerShell command to remove lock from the default path:
Remove-WebConfigurationLock -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/httpErrors/#defaultPath"
I know this is old as all heck. But what I've found is a lot of these modules and resources were built for specific tasks and are less modular than other DSC tools. You may have to create a custom resource that handles Remove-WebConfigurationLock in its set/get/test functions if you want a "pure" DSC solution. If not, a DSC script resource will do what you need.
Update - the original question claimed that I was able to successfully perform an Invoke-Command and then shortly after was unable to; I thought it was due to processes going on during login after a windows upgrade.
It turns out the PC was actually starting, running a quick batch/cmd file, and then restarting. This is what was leading to being able to do PS Remoting and then suddenly not. The restart was quick enough after first boot that I didn't realize it was happening. Sorry for the bad question.
For the curious, the machine was restarting because of a remnant of the Microsoft Deployment Toolkit in-place upgrade process. The way MDT completes its task-sequence post-upgrade is problematic for many reasons, and now I've got another to count.
Old details (no longer relevant, with incorrect assumption that machine was not restarting after first successful Invoke-Command):
I'm automating various things with VMs in Hyper-V using powershell and powershell remoting. I'll start up a VM and then want to run some commands on it via powershell.
I'm struggling with determining when I can safely start running the remote commands via things like Invoke-Command. I can't start immediately as I need to let the machine start up.
Right now I poll the VM with a one second sleep between calls until the following function returns $true:
function VMIsReady {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)][object]$VM
)
$heartbeat = $vm.Heartbeat
Write-Host "vm heartbeat is $heartbeat"
if (($heartbeat -eq 'OkApplicationsHealthy') -or ($heartbeat -eq 'OkApplicationsUnknown'))
{
try
{
Invoke-Command -VMName $vm.Name -Credential $(GetVMCredentials) {$env:computername} | out-null
}
catch [System.Management.Automation.RuntimeException]
{
Write-Host 'Caught expected automation runtime exception'
return $false
}
Write-Host 'remoting ready'
return $true
}
}
This usually works well; however, after a windows upgrade has happened, there are issues. I'll get Hyper-V remoting errors of various sorts even after VMIsReady returns $true.
These errors are happening while the VM is in the process of first user login after upgrade (Windows going through "Hi;We've got some updates for your PC;This might take several minutes-Don't turn off your PC). VMIsReady returns true right as this sequence starts - I imagine I probably should be waiting until the sequence is done, but I've no idea how to know when that is.
Is there a better way of determining when the machine is in a state where I can expect remoting to work without issue? Perhaps a way to tell when a user is fully logged on?
You can use Test-WSMan.
Of run a script on the invoke that will receive a response from the server.
[bool]$Response | Out-Null
try{
$Response = Invoke-Command -ComputerName Test-Computer -ScriptBlock {return $true}
}catch{
return $false
}
if ($Response -ne $true){
return $false
}else{
return $true
}
I've created a class called "Application" and loaded it in my main script with:
Import-Module -NAME "C:\PowerShell_Scripts\Class\Application.ps1" -GLOBAL -FORCE;
However if I ONLY make changes to the class file and run the code in PowerShell ISE none of the changes are applied. It's almost as if the class is still in memory even though I've used -FORCE.
I've also tried to remove the module before loading it and the same issue happens:
Remove-Module "Application" -ErrorAction Ignore -FORCE;
Import-Module -NAME "C:\PowerShell_Scripts\Class\Application.ps1" -GLOBAL -FORCE;
If I make a single character change in my main script then it reloads the class! But I shouldn't have to modify the main script to force PowerShell to reload the class, that just seems silly.
Is there a way to remove the Application class from memory if it exists?
NOTE: Files with just functions in them work file. This only applies to Class imports.
Addition: In the console, if I run the Remove-Module command it runs successfully but I can STILL create new objects with:
$appDetails = [Application]::new($applicationID);
Doesn't make sense to me...
MAIN SCRIPT:
# Application Details
# -----------------
#ID
$applicationID = 1;
############################################
#
# Load Supporting Scripts
#
############################################
try
{
Remove-Module "Application" -ErrorAction Ignore -FORCE;
Remove-Module "Common" -ErrorAction Ignore -FORCE;
Remove-Module "ServerData" -ErrorAction Ignore -FORCE;
Import-Module -NAME "C:\PowerShell_Scripts\Common.ps1" -GLOBAL -FORCE;
Import-Module -NAME "C:\PowerShell_Scripts\ServerData.ps1" -GLOBAL -FORCE;
Import-Module -NAME "C:\PowerShell_Scripts\Class\Application.ps1" -GLOBAL -FORCE;
}
catch
{
Write-Host "`nError: Cannot load required PowerShell scripts. Ensure C:\PowerShell_Scripts\ exists and has the required files." -ForegroundColor Red;
EXIT;
}
############################################
#
# Load the SharePoint Snapin Module.
#
############################################
LoadSharePointModule;
############################################
#
# Display component details to user.
#
############################################
#Create object of "Application" to get app details based on the ID.
$appDetails = [Application]::new($applicationID);
Write-Host "Ending ......";
APPLICATION CLASS FILE
Class Application
{
#Class Properties
[STRING] $appName;
[INT32] $appID;
[INT32] $versionMajor;
[INT32] $versionOS;
[INT32] $versionCentraAdmin;
[INT32] $versionMain;
[INT32] $versionGUI;
[INT32] $versionWorkflow;
[INT32] $versionForm;
[INT32] $versionVS;
[INT32] $versionOther;
[INT32] $versionFull;
[OBJECT] $spDevSite;
[OBJECT] $versionList;
#Constructor: Setup class properties.
Application ([INT32] $appID)
{
Write-Host "`nGathering application details ..." -ForegroundColor Yellow;
try
{
#Get the SharePoint Developer site Object.
$this.spDevSite = Get-SPWeb -ErrorAction Stop $GLOBAL:spDevURL;
}
catch
{
Write-Host "`nUnable to connect to SharePoint Developer site!: $($GLOBAL:spDevURL)";
#EXIT;
}
#Assign class property.
$this.appID = $appID;
}
}
I have deliberately set the URL for $GLOBAL:spDevURL; so that the Constructor fails for this test. It fails normally and displays
Write-Host "`nUnable to connect to SharePoint Developer site!: $($GLOBAL:spDevURL)";
But if I make a change to this line and run the script, the change is not applied.
The Known Issue
There is a known issue in PowerShell 5.0 and 5.1 that explains this behavior. The issue was acknowledged by DongBo Wang on the PowerShell 6 team in November 2016. He wrote the following:
"The module analysis result is stored in a cache with the module file path as the key and the PSModuleInfo object as the value. The cache entries are not properly invalidated based on the LastWriteTime of the module file, and thus same cached value got reused."
In other words, PowerShell 5.0, 5.1, and 6.0 keeps (and uses) old copies of classes in memory when it shouldn't.
Implications
This issue causes considerable problems for development using PowerShell classes if you do not compensate for it. I wrote a test that covers about 100 of the scenarios where class reloading is important. Vaguely speaking, in about 17 of those scenarios PowerShell 5.0 and 5.1 doesn't reload the class when it should. This means using the same session across edits creates a real likelihood the interpreter will have cached duplicate copies of the same or similar classes. That makes behavior unpredictable and causes strange results that cannot be troubleshot.
Workaround
I have found that you can still be productive developing using PowerShell classes. You just need to perform each test run in a fresh PowerShell session when a project involves PowerShell classes whose source the PowerShell interpreter may consider to have changed. The customary way to do this is to invoke your test command from your PowerShell console by invoking powershell.exe:
powershell.exe -Command { Invoke-Pester }
That's not a terribly inefficient test-edit-test cycle if you've got tight unit tests. If you need to step through code, you'll need to launch a fresh copy of ISE each time you make an edit.
With this workaround, I have found the productivity impact of this bug to be manageable. I developed this and this entirely using this workaround. Each of those projects involve a significant amount of code involving PowerShell classes.
Glossary:
Host: PowershellHost session
Interactive: [Environment]::UserInteractive -eq $True
Scenario:
Create a powershell module that only will abort propertly and without error on failed condition. In this case, some commands/modules only work properly in full interactive hosts, like ISE and Console, but not in fake interactive hosts like NuGet Package Manager Console
Failed Solution:
# Add value to Powershell manifest(psd1)
# Issue: Only supports a string for the `PowerShellHostName` property. How to specify both `ConsoleHost` and `Windows PowerShell ISE Host`? Unknown if this property supports regex, and even if it does, will the behavior change since it's not documented?
#{
....
# Name of the Windows PowerShell host required by this module
# PowerShellHostName = ''
....
}
Failed Solution:
# Check for interactive shell
# Issue: UserInteractive is still set in embedded shells like NuGet package manager
# console. Commands that expect user input directly often hang.
if([Environment]::UserInteractive) {
# Do stuff, dotsource, etc
}
Failed Solution:
# Issue: returning still leaves module loaded, and it appears in Get-Module list
# Even if value is supplied for return, powershell's return statement is 'special'
# and the value is ignored
if($Host.Name -inotmatch '(ConsoleHost|Windows PowerShell ISE Host)') {
Write-Warning "Host [$($Host.Name)] not supported, aborting"
return
}
Failed Solution:
# Issue: Module isn't loaded, so it can't be unloaded
if( $Host.Name -inotmatch '(ConsoleHost|Windows PowerShell ISE Host)' ) {
Remove-Module ThisModuleName
}
Failed Solution:
# Issue: Powershell module error output is just passthrough, import-module
# still reports success, even though $Error is has more stuff than before
if( $Host.Name -inotmatch '(ConsoleHost|Windows PowerShell ISE Host)' ) {
Write-Error "Unsupported Host:" $Host.Name
}
Annoying solution:
# Issue: Leaves two errors on the stack, one for the throw, one for the module not
# loading successfully
if($Host.Name -inotmatch '(ConsoleHost|Windows PowerShell ISE Host)') {
throw "Host [$($Host.Name)] not supported, aborting"
}
Not a solution:
Force user to wrap the import every time.
Questionable Solution:
Split module into nested submodules, one for 'Common', and one for each supported Host. Use subfolder for each, and duplicate psd1 for each. Looks like it will end up being a maintainability nightmare, especially with respect to nested dependencies.
UberModule
/ModuleCommon
/ModuleCommon.(psd1|psm1)
/ConsoleHostSpecific
/ConsoleHostSpecific.(psd1|psm1)
/IseHostSpecific
/IseHostSpecific.(psd1|psm1)
/etc...
Is there a better way to do this, or is the uber-module split the only way to go?
Take a look at the #requires keyword, it may offer a few options that you have not tried yet. I don't know if NuGet Package Manager Console has a unique ShellId or not.
#Requires –ShellId Microsoft.PowerShell
http://technet.microsoft.com/en-us/library/hh847765.aspx
My MSMQ queue gets created by PowerShell DSC engine. I can see queues created. Since DSC engine runs from SYSTEM account, then queue owner also gets set to SYSTEM.
When I try to set MSMQ ACL from PowerShell console I constantly get following error:
PS C:\Users\Administrator.DOMAIN> whoami; Get-MsmqQueue queue1 | Set-MsmqQueueACL -UserName "Everyone" -Allow FullControl
DOMAIN\administrator
Set-MsmqQueueACL : Failed to set security descriptor. Error code: 3222143013
At line:1 char:50
+ whoami; Get-MsmqQueue incredipay_atm_processor | Set-MsmqQueueACL -UserName "Eve ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidResult: (FullControl:MessageQueueAccessRights) [Set-MsmqQueueACL], Win32Exception
+ FullyQualifiedErrorId : Failed to set security descriptor. Error code: 3222143013,Microsoft.Msmq.PowerShell.Commands.SetMSMQQueueACLCommand
I also can't set MSMQ ACL using custom DSC resource, which is basically doing the same thing only from SYSTEM account.
So the question is are there any way to set MSMQ permissions from within PowerShell DSC engine using Set-MSMQQueueACL cmdlet. Or at least if I'll be able to solve previously mentioned mentioned error, then maybe I'll be able to solve also DSC problem.
I'm running Windows 2012 and WMF 4.0.
Thanks in advance.
I did something similar recently and hit the same problem. You have to take ownership of the queue first (admin rights required), and then you can change the permissions.
Try these manual steps in the Computer Management snap-in first to check it solves your error, and then work out how to reproduce it via PowerShell.
Start -> Run -> compmgmt.msc
Expand "Computer management (Local) -> Services and Applications -> Message Queuing -> Private Queues"
Right click -> Properties -> Security -> Advanced -> Owner -> Other users or groups...
Enter your user name (DOMAIN\administrator)
Click OK, then OK again
You should now be able to edit security via script
I ended up writing some PInvoke code to take ownership of the queue using C#, which I compiled on the fly with Add-Type in PowerShell. I can't share it unfortunately as it's proprietary, but this question might give you some pointers:
How do I set the owner of a message queue?
P.S. error code 3222143013 is 0xC00E0025, which translates to MQ_ERROR_ACCESS_DENIED (see http://msdn.microsoft.com/en-us/library/ms700106%28v=vs.85%29.aspx)
I've managed to overcome this issue by using following code in my custom DSC resource:
$ScriptBlock={
param(
[String] $QueueName,
[String] $Username,
[String[]] $MessageQueueAccessRight,
[ValidateSet("Allow","Deny")]
[String] $MessageQueueAccessType
)
$params = #{}
$queue = Get-MSMQQueue -Name $QueueName
$params.Add("InputObject",$queue)
$params.Add("Username",$Username)
switch ($MessageQueueAccessType)
{
"Allow" {$params.Add("Allow","$MessageQueueAccessRight"); Break;}
"Deny" {$params.Add("Deny","$MessageQueueAccessRight"); Break;}
}
Set-MsmqQueueACL #params
}
Foreach($MessageQueueAccessRight in $MessageQueueAccessRights)
{
Invoke-Command -ScriptBlock $ScriptBlock -ComputerName . -Credential $DomainAdministratorCredential -ArgumentList $QueueName,$Username,$MessageQueueAccessRight,$MessageQueueAccessType
}
Of course it's necessary to use the same approach when MSMQ queue gets created by DSC. So MSMQ queue creation should be made by the same account, whose initially going to adjust ACLs.
To do this in DSC, you can run your command using different credentials by having your custom DSC resource take a [PSCredential] parameter.
To do this securely requires some significant changes to your DSC infrastructure. See my answer to this question: https://serverfault.com/questions/632390/protecting-credentials-in-desired-state-configuration-using-certificates/#632836
If you just want to test before making those changes, you can tell DSC to allow storing your credentials in plaintext using PSDscAllowPlainTextPassword = $true in your configuration data (see here for details).
I also created a custom DSC resource to setup/modify my MSMQ queues within my web farm. Since DSC runs as SYSTEM you must ensure that the SYSTEM account has access to create/modify MSMQ's on the node.
There is a way to have DSC run as an account. If that is the case then you have to ensure that you are passing in that account when attempting to create/modify your MsmqQueue.
I understand I am responding to an old thread. But someone else in the near future may be facing the same issue and come across this thread.
Enjoy & Good Luck!