Hello everyone, my name is David Loder, long-time reader, first-time PFE blogger, based out of Detroit, Michigan. And I have a confession to make. I hate servers. Or more precisely I hate logging in to servers.
Tuesday, January 3, 2017
Remote Server Administration Tools for Windows 10
Hello everyone, my name is David Loder, long-time reader, first-time PFE blogger, based out of Detroit, Michigan. And I have a confession to make. I hate servers. Or more precisely I hate logging in to servers.
First PFE Blog Entry
Wednesday, December 9, 2015
Logging PowerShell Error Message Output
$A = Get-ChildItem C:\temp
try {
$B = Get-ChildItem2 C:\temp
} catch {
$Error[0] | Out-File -filepath c:\temp\test.log
}
I have forced an error to occur on line 3 by including an invalid command. When run interactively no output is produced on the screen, which is fine because when run as a scheduled task, no one will be there to see it anyways. The log file contains the following content:
Get-ChildItem2 : The term 'Get-ChildItem2' is not recognized as the name of a cmdlet, function, script file, orThe output log contains the normal error detail you'd see if running the command interactively.
operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try
again.
At C:\Temp\test.ps1:3 char:7
+ $B = Get-ChildItem2 C:\temp
+ ~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Get-ChildItem2:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Let's enhance the log with something normal like a timestamp.
(get-date -UFormat "%Y%m%d%H%M%S") + ": " + $Error[0] | Out-File -filepath c:\temp\test.log
The output log now contains the content:
20151209123349: The term 'Get-ChildItem2' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.We successfully added the timestamp, but lost many details such as the line that incurred the error. Let's see if we can restore the missing data to the log file. When we look at the error object, we can see the following information:
PS C:\Temp> $Error[0] | gm
TypeName: System.Management.Automation.ErrorRecord
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetObjectData Method void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System....
GetType Method type GetType()
ToString Method string ToString()
CategoryInfo Property System.Management.Automation.ErrorCategoryInfo CategoryInfo {get;}
ErrorDetails Property System.Management.Automation.ErrorDetails ErrorDetails {get;set;}
Exception Property System.Exception Exception {get;}
FullyQualifiedErrorId Property string FullyQualifiedErrorId {get;}
InvocationInfo Property System.Management.Automation.InvocationInfo InvocationInfo {get;}
PipelineIterationInfo Property System.Collections.ObjectModel.ReadOnlyCollection[int] PipelineIterationInfo {g...
ScriptStackTrace Property string ScriptStackTrace {get;}
TargetObject Property System.Object TargetObject {get;}
PSMessageDetails ScriptProperty System.Object PSMessageDetails {get=& { Set-StrictMode -Version 1; $this.Except...
We have a default to ToString() method. Let's see what that gets us:
PS C:\Temp> $Error[0].ToString()
The term 'Get-ChildItem2' is not recognized as the name of a cmdlet, function, script file, or operable program. Check
the spelling of the name, or if a path was included, verify that the path is correct and try again.
That matches the text that ended up in the second log file. Let's see if we can find where the missing data lives in the ErrorRecord object. The initial output contains the keywords CategoryInfo and FullyQualifiedErrorId. Those are both properties, so let's look at those:
PS C:\Temp> $Error[0].CategoryInfo
Category : ObjectNotFound
Activity :
Reason : CommandNotFoundException
TargetName : Get-ChildItem2
TargetType : String
PS C:\Temp> $Error[0].FullyQualifiedErrorId
CommandNotFoundException
That's a start as that data is included in the initial output, but is unformatted. We're still missing the pointer to the faulting line in the script. Looking through the other properties, we see that the InvocationInfo property contains the data we were looking for in the PositionMessage property.
PS C:\Temp> $Error[0].InvocationInfo
MyCommand :
BoundParameters : {}
UnboundArguments : {}
ScriptLineNumber : 3
OffsetInLine : 7
HistoryId : 36
ScriptName : C:\Temp\test.ps1
Line : $B = Get-ChildItem2 C:\temp
PositionMessage : At C:\Temp\test.ps1:3 char:7
+ $B = Get-ChildItem2 C:\temp
+ ~~~~~~~~~~~~~~
PSScriptRoot : C:\Temp
PSCommandPath : C:\Temp\test.ps1
InvocationName : Get-ChildItem2
PipelineLength : 0
PipelinePosition : 0
ExpectingInput : False
CommandOrigin : Internal
DisplayScriptPosition :
At this point, we could come up with a mildly complex function to recreate the initial output by referencing the appropriate properties. However, there's an easier way to get the error output.
Out-String
In all my years of PowerShell-ing, I've never had a reason to use Out-String, as most items will normally render as a string to the console or a file (as was evidenced above). Out-String does exactly what we want in this instance.
PS C:\Temp> $Error[0] | Out-String
Get-ChildItem2 : The term 'Get-ChildItem2' is not recognized as the name of a cmdlet, function, script file, or
operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try
again.
At C:\Temp\test.ps1:3 char:7
+ $B = Get-ChildItem2 C:\temp
+ ~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Get-ChildItem2:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
From the console, this output is unsurprisingly identical to the statement $Error[0] issued all by itself.
We can add this into our script as follows:
(get-date -UFormat "%Y%m%d%H%M%S") + ": " + ($Error[0] | Out-String) | Out-File -filepath c:\temp\test.logAnd finally our log file contains the full content we want:
20151209132608: Get-ChildItem2 : The term 'Get-ChildItem2' is not recognized as the name of a cmdlet, function, script file, or
operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try
again.
At C:\Temp\test.ps1:3 char:7
+ $B = Get-ChildItem2 C:\temp
+ ~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Get-ChildItem2:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Thursday, May 28, 2015
Deploy IIS URL Rewrite rules using PowerShell
It took a little while to figure out the syntax of the rewrites and how to deploy via PowerShell. Most of the examples I found only provided the XML definition of the rule. Here are those two rules, ready for deployment via PowerShell (using the IIS PowerShell Snap-In).
Example: Redirect to HTTPS
$SiteName = "FIMSite"
$RuleName = "HTTP to HTTPS"
$Rule = @{
Name = $RuleName
patternSyntax = 'ECMAScript'
stopProcessing = 'True'
match = @{
url = '(.*)'
ignoreCase = 'True'
negate = 'False'
}
conditions = @{
logicalGrouping = 'MatchAll'
trackAllCaptures = 'True'
}
action = @{
type = 'Redirect'
url = 'https://{HTTP_HOST}/{R:1}'
appendQueryString = 'False'
redirectType = 'Permanent'
}
}
Add-WebConfigurationProperty -PSPath "IIS:\Sites\$SiteName" -Filter "/system.webServer/rewrite/rules" -Name "." -Value $Rule
$match = @{
input = '{HTTPS}'
matchType = 'Pattern'
pattern = 'off'
ignoreCase = 'True'
negate = 'False'
}
Add-WebConfigurationProperty -PSPath "IIS:\Sites\$SiteName" -Filter "/system.webServer/rewrite/rules/rule[@Name='$RuleName']/conditions" -Name "." -Value $match
Example: Redirect to Application
$SiteName = "FIMSite"
$RuleName = "Redirect to FIM Application"
$Rule = @{
Name = $RuleName
patternSyntax = 'ECMAScript'
stopProcessing = 'True'
match = @{
url = '^$'
ignoreCase = 'True'
negate = 'False'
}
action = @{
type = 'Redirect'
url = '/IdentityManagement/default.aspx'
appendQueryString = 'False'
redirectType = 'Permanent'
}
}
Add-WebConfigurationProperty -PSPath "IIS:\Sites\$SiteName" -Filter "/system.webServer/rewrite/rules" -Name "." -Value $Rule
Monday, September 29, 2014
Fun ways to shoot yourself in your own foot
Step 2. Add lots of hosts entries to your servers to cover up problems with your DNS usage.
Step 3. Forget about Step 2.
Step 4. Have a failure that exercises the engineered solution in Step 1.
Step 5. Spend the next several hours wondering why your service isn't working before blaming the directory and calling in the experts because one error message happens to include the word "logon".
Monday, August 26, 2013
Sending Encrypted S/MIME Messages with PowerShell
$RecipientCN='someusername'Now that we have a valid certificate, compose and send the email. The forum message at http://social.msdn.microsoft.com/Forums/en-US/74e4711e-1f66-43a7-9e3b-bc9cfbcd1b73/sending-smime-encrypted-email-using-c provided most of the basis for getting the encryption correct.
$RootDSE = [ADSI]("LDAP://RootDSE")
$SearchForestForPerson = New-Object DirectoryServices.DirectorySearcher
$SearchForestForPerson.SearchRoot = "GC://" + $RootDSE.rootDomainNamingContext
$SearchForestForPerson.SearchScope = "subtree"
$SearchForestForPerson.PropertiesToLoad.Add("distinguishedname") | Out-Null
$SearchForestForPerson.PropertiesToLoad.Add("mail") | Out-Null
$SearchForestForPerson.PropertiesToLoad.Add("usercertificate") | Out-Null
$SearchForestForPerson.Filter = ("(&(objectClass=person)(CN=$RecipientCN))")
$Recipient = $SearchForestForPerson.FindOne()
$ChosenCertificate = $null
$Now = Get-Date
If ($Recipient.Properties.usercertificate -ne $null) {
ForEach ($UserCertificate in $Recipient.Properties.usercertificate) {
$ValidForSecureEmail = $false
$Certificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]$UserCertificate
$Extensions = $Certificate.Extensions
ForEach ($Extension in $Extensions) {
If ($Extension.EnhancedKeyUsages -ne $null) {
ForEach ($EnhancedKeyUsage in $Extension.EnhancedKeyUsages) {
If ($EnhancedKeyUsage.FriendlyName -eq "Secure Email") {
$ValidForSecureEmail = $true
break
}
}
If ($ValidForSecureEmail) {
break
}
}
}
If ($ValidForSecureEmail) {
If ($Now -gt $Certificate.NotBefore.AddMinutes(-5) -and $Now -lt $Certificate.NotAfter.AddMinutes(5)) {
$ChosenCertificate = $Certificate
}
}
If ($null -ne $ChosenCertificate) {
break
}
}
}
Add-Type -assemblyName "System.Security"
$MailClient = New-Object System.Net.Mail.SmtpClient "relay.widgets.com"
$Message = New-Object System.Net.Mail.MailMessage
$Message.To.Add($Recipient.properties.mail.item(0))
$Message.From = "noreply@widgets.com"
$Message.Subject = "Unencrypted subject of the message"
$Body = "Encrypted body of the message"
$MIMEMessage = New-Object system.Text.StringBuilder
$MIMEMessage.AppendLine('Content-Type: text/plain; charset="iso-8859-1"') | Out-Null
$MIMEMessage.AppendLine('Content-Transfer-Encoding: 7bit') | Out-Null
$MIMEMessage.AppendLine() | Out-Null
$MIMEMessage.AppendLine($Body) | Out-Null
[Byte[]] $BodyBytes = [System.Text.Encoding]::ASCII.GetBytes($MIMEMessage.ToString())
$ContentInfo = New-Object System.Security.Cryptography.Pkcs.ContentInfo (,$BodyBytes)
$CMSRecipient = New-Object System.Security.Cryptography.Pkcs.CmsRecipient $ChosenCertificate
$EnvelopedCMS = New-Object System.Security.Cryptography.Pkcs.EnvelopedCms $ContentInfo
$EnvelopedCMS.Encrypt($CMSRecipient)
[Byte[]] $EncryptedBytes = $EnvelopedCMS.Encode()
$MemoryStream = New-Object System.IO.MemoryStream @(,$EncryptedBytes)
$AlternateView = New-Object System.Net.Mail.AlternateView($MemoryStream, "application/pkcs7-mime; smime-type=enveloped-data;name=smime.p7m")
$Message.AlternateViews.Add($AlternateView)
$MailClient.Send($Message)
Tuesday, June 18, 2013
FIM CSExport fails to run as a scheduled task
In our FIM environment we want to retrieve the list of pending exports. This is typically accomplished with a “csexport.exe MAName /f:x” command. On my dev environment as a user with FIMSyncAdmins rights, the export is produced as expected.
When I ran the command as a scheduled task I would receive this error message in our log file
Microsoft Identity Integration Server Connector Space Export Utility v4.1.3419.0
c 2012 Microsoft Corporation. All rights reservedFailed to export connector space.
Error: <error>The Synchronization Service Manager service has stopped.</error>
It’s an odd message in that the Sync service was absolutely running.
After making sure there was nothing wrong with the PowerShell script that was driving the command, I eventually tried elevating the service account to include local admin rights, and at that point it succeeded.
From there I took a leap of faith that the Sync service account really was running, but under a non-admin scheduled task, it couldn’t see that the service was running for some reason. Recalling the Service Control Manager hardening from Windows Server 2003 SP1, I was guessing the scheduled task couldn’t query the SCM.
Sure enough, reviewing the default ACLs
C:\Windows>sc sdshow FIMSynchronizationService
D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCR
RC;;;IU)(A;;CCLCSWLOCRRC;;;SU)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)
As an Interactive User (IU) you get read access to the service. But as a scheduled task you don’t get the IU SID. The solution is to grant the FIMSyncAdmins group read access to the service.
With a small PowerShell script to determine the local group’s SID, we can update the SDDL for the service
C:\Windows>sc sdset FIMSynchronizationService D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;
CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;S-1-5-2
1-2974223652-3803999246-3267058373-1009)(A;;CCLCSWLOCRRC;;;SU)S:(AU;FA;CCDCLCSWR
PWPDTLOCRSDRCWDWO;;;WD)
[SC] SetServiceObjectSecurity SUCCESS
And now CSExport works from a scheduled task.
Wednesday, November 7, 2012
Disable DHCP Authorization Check
Redocumenting this here so I don’t forget (as it just came up again).
Use the HKLM\System\CurrentControlSet\Services\DHCPServer\Parameters DisableRogueDetection REG_DWORD = 1 from http://support.microsoft.com/kb/297847.
Original discussion of the topic is archived at http://www.activedir.org/ListArchives/tabid/55/forumid/1/postid/30880/view/topic/Default.aspx.
Thursday, November 1, 2012
Conditionally exporting Null values using IIF in FIM
I’m re-documenting this here so I don’t forget about it again.
FIM will not, under any circumstance, export a Null value as the result of an IIF statement in an outbound flow!
IIF is broken, and has been broken from the initial FIM release. See Clear/delete attribute through Synchronization Rule and Using IIF to conditionally flow an authoritative attribute delete.
So I see three possible solutions to this problem.
- Classic coded sync rules for attributes with conditional export logic.
- Denormalize the metaverse so that all calculations can be performed inbound and the result stored in the metaverse, then use only direct export flows.
- (And the one I'm choosing at the moment) Perform a direct flow of a NullString attribute in the outbound flow to ensure that nulls are written. Then add dependent sync rules for each condition that flow the calculated value when the condition is met. This can be a bit messy to look at in FIM Service as dependent sync rules can only use logical AND when constructing the scope filters, so OR must be expressed as additional dependent sync rules, which can greatly increase the number of rules required. In outbound flows, the last writer wins. The null value in the parent rule is first. If the condition is met, the dependent rule will write its value second, thus becoming the winning value that is exported. When the condition ceases to be met the direct null value flow from the parent is allowed to win again and becomes the exported value.
UPDATE (2012.11.07)
Hotfix rollup 4.1.2548.0 from http://support.microsoft.com/kb/2750671 includes a fix to IIF. I haven’t tested it yet, but good news for once on one of my FIM DCRs.
Tuesday, October 16, 2012
ADFS 2.0 Event ID 248 and 364: An unsecured or incorrectly secured fault was received
We had our first significant outage with ADFS this weekend. During a Sunday morning change control we updated the communication certificates on all our STS and Proxy servers and promoted a newer signing certificate from secondary to primary, following the directions at AD FS 2.0: How to Replace the SSL, Service Communications, Token-Signing, and Token-Decrypting Certificates. As our PKI infrastructure was recently changed the new signing certificate chained up to a new root, but all of our Dev and QA tests were successful on the new chain.
All changes tested out successfully; our relying parties that only trust one certificate had switched to trusting the new signing certificate and users could still access the relying parties. So the change control was closed.
Monday morning we received notification that users connecting externally were receiving an error message rather than getting to the Forms-Based Logon page. What was odd for this outage was that all our internal access to ADFS was fine, it was only external access through the proxy servers having issues.
The proxy servers ADFS logs were filling with Event ID 364 errors:
Encountered error during federation passive request.
Additional Data
Exception details:
System.ServiceModel.Security.MessageSecurityException: An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail. ---> System.ServiceModel.FaultException: An error occurred when verifying security for the message.
--- End of inner exception stack trace ---Server stack trace:
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.ProcessReply(Message reply, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at Microsoft.IdentityServer.Protocols.PolicyStore.IPolicyStoreReadOnlyTransfer.GetState(String serviceObjectType, String mask, FilterData filter, Int32 clientVersionNumber)
at Microsoft.IdentityServer.PolicyModel.Client.PolicyStoreReadOnlyTransferClient.GetState(String serviceObjectType, String mask, FilterData filter, Int32 clientVersionNumber)
at Microsoft.IdentityServer.ProxyConfiguration.ProxyConfigurationReader.FetchServiceSettingsData()
at Microsoft.IdentityServer.ProxyConfiguration.ProxyConfigurationReader.GetServiceSettingsData()
at Microsoft.IdentityServer.ProxyConfiguration.ProxyConfigurationReader.GetFederationPassiveConfiguration()
at Microsoft.IdentityServer.Web.PassivePolicyManager.GetPassiveEndpointAbsolutePath()
at Microsoft.IdentityServer.Web.FederationPassiveAuthentication.GetPassiveEndpointAbsolutePath()System.ServiceModel.FaultException: An error occurred when verifying security for the message.
Our first troubleshooting activity was to restart the ADFS service on the proxy server. When we did that it logged an Event ID 248 error:
The federation server proxy was not able to retrieve the list of endpoints from the Federation Service at corp.sts.WIDGETS.com. The error message is 'An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.'.
User Action
Make sure that the Federation Service is running. Troubleshoot network connectivity. If the trust between the federation server proxy and the Federation Service is lost, run the Federation Server Proxy Configuration Wizard again.
Frustratingly, no inner FaultException was present.
We re-ran the Federation Server Proxy Configuration Wizard and it completed successfully but the same 248 error occurred at service start. We also verified the new signing cert did chain up to a root that the proxy server trusted. Turning up full debug on the proxy server did not provide any additional useful data.
On a functional proxy server one expects service start to result in Event ID 245
The federation server proxy retrieved the following list of endpoints from the Federation Service at 'https://corp.sts.WIDGETS.com:443/adfs/services/proxytrustpolicystoretransfer':
/FEDERATIONMETADATA/2007-06/FEDERATIONMETADATA.XML
/ADFS/SERVICES/TRUST/MEX
/ADFS/SERVICES/TRUST/2005/WINDOWSTRANSPORT
/ADFS/SERVICES/TRUST/2005/CERTIFICATEMIXED
/ADFS/SERVICES/TRUST/2005/CERTIFICATETRANSPORT
/ADFS/SERVICES/TRUST/2005/USERNAMEMIXED
/ADFS/SERVICES/TRUST/2005/ISSUEDTOKENMIXEDASYMMETRICBASIC256
/ADFS/SERVICES/TRUST/2005/ISSUEDTOKENMIXEDSYMMETRICBASIC256
/ADFS/SERVICES/TRUST/13/CERTIFICATEMIXED
/ADFS/SERVICES/TRUST/13/USERNAMEMIXED
/ADFS/SERVICES/TRUST/13/ISSUEDTOKENMIXEDASYMMETRICBASIC256
/ADFS/SERVICES/TRUST/13/ISSUEDTOKENMIXEDSYMMETRICBASIC256
To help isolate the problem we configured a local hosts entry on the proxy server to bypass the load balancers and hit a single STS. We could NetMon trace the service start and see the SSL handshake and traffic going only to the expected internal STS. As we saw no traffic trying to go anywhere except to the STS we were fairly certain there wasn’t an issue with validating the new chain. But given the error message for receiving an incorrectly secured response and that we just changed all the certificates we were fairly certain the switchover was the problem, but we had yet to figure out the solution.
Finally we decided to try restarting the ADFS service on the STS the proxy server was using, even though that STS was not exhibiting any errors. So we restarted the STS and restarted the proxy, and the proxy service started without error. SUCCESS!!
We restarted the service on the other STSs in the pool and restarted our other proxy server and it started working as well.
My guess for what happened is that the proxy servers reached their 4 hour trust renewal cycle after the change control verification had completed. At that time, I am guessing the SOAP responses to the proxytrustpolicystoretransfer endpoint requests were still being signed with the old signing certificate when the proxy was expecting them to be signed with the new, hence the “incorrectly secured” error. I’m guessing the service restart forced the STS to pick the new certificate to use to sign its SOAP responses for the proxytrustpolicystoretransfer endpoint. I’m also guessing we missed this in Dev and QA because the proxy usage is a secondary use case and was likely tested after a service restart or server reboot on the STS.
We’re still waiting on our Microsoft PFE to return with root cause analysis to see if Microsoft acknowledges a bug in the certificate handling. But for now, the short story is to cycle the STS services when rolling to new certificates.
UPDATE (2012.11.07)
I have updated the instructions in the AD FS 2.0: How to Replace the SSL, Service Communications, Token-Signing, and Token-Decrypting Certificates wiki article to include the STS service restart step.
