Add permissions to a Session Configuration


Though the recommended approach would be to upgrade to PowerShell 5.1 and implement JEA (preferable with DSC and the JEA DSC module), there sometimes might be a need to programmatically add permissions to a PowerShell session configuration.

Continuing the mentioned above, and a question asked on the reddit forum, below is an example on how to add a specific permission (ACE) to the configuration session permissions (ACL):

# The identity to add permissions for
$Identity = "myDomain\nonAdmins"
# The configuration name to change permissions to (default is 'microsoft.powershell')
$sessionConfigurationName = 'microsoft.powershell'
# Get the current permissions on the default endpoint
$sddl = (Get-PSSessionConfiguration -Name $sessionConfigurationName).SecurityDescriptorSddl
# Build the new Access Control Entry object
$rights = -1610612736 # AccessAllowed
$IdentitySID = ((New-Object -TypeName System.Security.Principal.NTAccount -ArgumentList $Identity).Translate(
[System.Security.Principal.SecurityIdentifier])).Value
$newAce = New-Object System.Security.AccessControl.CommonAce(
[System.Security.AccessControl.AceFlags]::None,
[System.Security.AccessControl.AceQualifier]::AccessAllowed,
$rights, $IdentitySID, $false, $null
)
# Prepare the RawSecurityDescriptor
$rawSD = New-Object -TypeName System.Security.AccessControl.RawSecurityDescriptor -ArgumentList $sddl
if ($rawSD.DiscretionaryAcl.GetEnumerator() -notcontains $newAce) {
$rawSD.DiscretionaryAcl.InsertAce($rawSD.DiscretionaryAcl.Count, $newAce)
}
$newSDDL = $rawSD.GetSddlForm([System.Security.AccessControl.AccessControlSections]::All)
# Set the PSSessionConfiguration permissions
Set-PSSessionConfiguration -Name $sessionConfigurationName -SecurityDescriptorSddl $newSDDL
# Verify permissions were added
(Get-PSSessionConfiguration -Name $sessionConfigurationName).Permission -split ', '

HTH,
Martin.

Using DSC with the WinRM service disabled


 

Every once in a while I come across a weird scenario where the customer’s requirements and/or limitations are so challenging that they take me to a interesting discovery journey.

This time, the requirement was to use Desired State Configuration (DSC) in remote machines where there was no connectivity (with any protocol or port) to them from the central management point except with their proprietary agents. And the most strict requirement was to have the WinRM service locally disabled. So how can we use DSC to configure the machines and have them constantly compliant, when the simple Start-DSCConfiguration cmdlet fails because the WinRM service is disabled?

We ended up using their agent to transmit text commands, where those commands would include the content of the mof files as a byte array, then write the bytes to a local mof file and initiate a CIM method to start the configuration. The last part is pretty much the same as what Start-DscConfiguration does.

Below are snippets of the code I used for this:

 

#region ### BUILD THE CONFIGURATION ###
# Declare the configuration:
Configuration TestDscWithoutWinRm {
Import-DscResource –ModuleName PSDesiredStateConfiguration
node localhost {
File akada {
Ensure = 'Present'
Type = 'File'
Contents = 'Martin was here!'
DestinationPath = 'C:\Temp\test.log'
}
}
}
# Run the configuration to create the MOF file:
TestDscWithoutWinRm
#endregion
#region ### APPLY THE CONFIGURATION FOR THE FIRST TIME ###
# This will NOT work without the WinRM service running:
Start-DscConfiguration -Wait -Verbose -Path .\TestDscWithoutWinRm
# This is one a workaround for the first apply:
Copy-Item -Path .\TestDscWithoutWinRm\localhost.mof C:\Windows\System32\Configuration\pending.mof -Force
Invoke-CimMethod -Namespace root/Microsoft/Windows/DesiredStateConfiguration -ClassName MSFT_DSCLocalConfigurationManager -Method PerformRequiredConfigurationChecks -Arguments @{Flags = [System.UInt32]1}
# This is another workaround for the first apply:
$configData = [byte[]][System.IO.File]::ReadAllBytes((Resolve-Path -Path '.\TestDscWithoutWinRm\localhost.mof'))
Invoke-CimMethod -Namespace root/Microsoft/Windows/DesiredStateConfiguration -ClassName MSFT_DSCLocalConfigurationManager -Method SendConfigurationApply -Arguments @{ConfigurationData = $configData; force = $true}
#endregion
#region ### RE-APPLY THE CURRENT CONFIGURATION ###
# This is the workaround for re-applying the current configuration:
Copy-Item -Path C:\Windows\System32\Configuration\current.mof C:\Windows\System32\Configuration\pending.mof -Force
Invoke-CimMethod -Namespace root/Microsoft/Windows/DesiredStateConfiguration -ClassName MSFT_DSCLocalConfigurationManager -Method ApplyConfiguration -Arguments @{force = [bool]$true}
#endregion
#region ### TEST THE CONFIGURATION ###
# This doesn't work without the WinRM service:
Test-DscConfiguration
# This is a workaround:
Invoke-CimMethod -Namespace root/Microsoft/Windows/DesiredStateConfiguration -ClassName MSFT_DSCLocalConfigurationManager -Method TestConfiguration
#endregion

With all the above being said, the WinRM protocol is secured, especially if you are in a domain environment and the traffic is encrypted with the Kerberos ticket. You can also use https (configure SSL certificates in the plugins) to to have the traffic encrypted in a non-domain environment (e.g. DMZ, or cross domains with no trust). Unfortunately, too much (where not really needed) security is just an unpleasant overhead in administration.

 

HTH,
Martin.

Copy files to an Azure VM through PSRemoting


You may sometimes need to copy a file to a remote machine where the SMB (aka cifs) ports are closed, or the option to copy files through the RDP connection is disabled as well. Also, you may need to copy the file(s) as a step in an automated process.

If you have PowerShell 5.0 installed it’s easy as pie, since the Copy-Item cmdlet has a set of new parameters: -ToSession and -FromSession

First, create a PSSession object


$cred = Get-Credential
$sess = New-PSSession -ComputerName myVM.myLocation.cloudapp.azure.com -Port 5986 -Credential $cred

Then, use the Copy-Item cmdlet with the –ToSession parameter and pass the PSSession object you created in the previous step:


Copy-Item -Path C:Temptest.zip -Destination C:Temp -ToSession $sess

You can use the same technique to copy files from the remote VM:


Copy-Item -FromSession $sess -Path C:\Temp\test.zip -Destination C:\Temp

Note that the ToSession and FromSession parameters are mutually exclusive parameters (are not included in the same parameter set), meaning you cannot use them to copy files directly from one session to another.

 

 

HTH,
Martin.