tag:blogger.com,1999:blog-6988292460570371732024-03-13T04:31:02.294+01:00IS4U blogIdentifying with your businessRobinhttp://www.blogger.com/profile/04219206913960609880noreply@blogger.comBlogger44125tag:blogger.com,1999:blog-698829246057037173.post-72907300792370524302016-11-30T19:42:00.000+01:002016-11-30T19:42:49.271+01:00FIM2010 Troubleshooting: Stopped extension dll load<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, "Courier New", Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<h1>Intro</h1>
When uploading a new extension to an existing deployment and trying to sync, the error "Stopped-extension-dll-load" was occurring. The deployment was already using rules extensions, so I did not expect any issues with .Net. After some basic troubleshooting steps, checking the .Net build version of the extensions, restarting services, checking the event viewer I found a hint to the solution.
<a name='more'></a>
<h1>Issue</h1>
Following info was loggend in the eventviewer.
<!-- code formatted by http://manoli.net/csharpformat/ -->
<pre class="csharpcode">
<span class="kwrd"><</span><span class="html">Event</span> <span class="attr">xmlns</span><span class="kwrd">="http://schemas.microsoft.com/win/2004/08/events/event"</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">System</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">Provider</span> <span class="attr">Name</span><span class="kwrd">="FIMSynchronizationService"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">EventID</span> <span class="attr">Qualifiers</span><span class="kwrd">="49152"</span><span class="kwrd">></span>6300<span class="kwrd"></</span><span class="html">EventID</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">Level</span><span class="kwrd">></span>2<span class="kwrd"></</span><span class="html">Level</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">Task</span><span class="kwrd">></span>3<span class="kwrd"></</span><span class="html">Task</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">Keywords</span><span class="kwrd">></span>0x80000000000000<span class="kwrd"></</span><span class="html">Keywords</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">TimeCreated</span> <span class="attr">SystemTime</span><span class="kwrd">="2016-11-02T15:33:07.000000000Z"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">EventRecordID</span><span class="kwrd">></span>523064<span class="kwrd"></</span><span class="html">EventRecordID</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">Channel</span><span class="kwrd">></span>Application<span class="kwrd"></</span><span class="html">Channel</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">Computer</span><span class="kwrd">></span>FIM<span class="kwrd"></</span><span class="html">Computer</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">Security</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">System</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">EventData</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">Data</span><span class="kwrd">></span>
Could not load file or assembly 'file:///C:\Program Files\Microsoft Forefront Identity Manager\2010\Synchronization Service\Extensions\MVRulesExtension.dll' or one of its dependencies.
Operation is not supported. (Exception from HRESULT: 0x80131515)
at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.InternalLoadFrom(String assemblyFile, Evidence securityEvidence, Byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm, Boolean forIntrospection, Boolean suppressSecurityChecks, StackCrawlMark& stackMark) at System.Reflection.Assembly.LoadFrom(String assemblyFile)
at Microsoft.MetadirectoryServices.Impl.ScriptHost.InitializeWorker(InitializeArguments pArgs) InnerException=<span class="attr">&gt;</span>
An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework.
This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous.
If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch.
See http://go.microsoft.com/fwlink/?LinkId=155569 for more information.
InnerException=<span class="attr">&gt;</span> none
<span class="kwrd"></</span><span class="html">Data</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">EventData</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">Event</span><span class="kwrd">></span></pre>
<h1>Solution</h1>
After checking the <a href="http://go.microsoft.com/fwlink/?LinkId=155569">provided link</a> and googling more info using the inner exception quoted below, it was pretty clear how to solve it.
<blockquote>An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch.</blockquote>
The error went away after adding the <code>loadFromRemoteSources</code> switch to the runtime part of the FIM configuration file miiserver.exe.config, located in the "Microsoft Forefront Identity Manager\2010\Synchronization Service\Bin" folder.
<!-- code formatted by http://manoli.net/csharpformat/ -->
<pre class="csharpcode">
<span class="kwrd"><</span><span class="html">configuration</span><span class="kwrd">></span>
[...]
<span class="kwrd"><</span><span class="html">runtime</span><span class="kwrd">></span>
[...]
<span class="kwrd"><</span><span class="html">loadFromRemoteSources</span> <span class="attr">enabled</span><span class="kwrd">="true"</span><span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">runtime</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">configuration</span><span class="kwrd">></span></pre>
<h1>Conclusion</h1>
It's still a mystery why this issue would arise after adding a new dll to a deployment that was already using extensions for a year without problems. Hopefully will this article be helpful for someone having the same cause for his stopped-extension-dll-load error. Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-81533641041129754642016-10-31T22:35:00.001+01:002016-10-31T22:35:54.619+01:00MIM 2016: RCDC Management with PowerShell<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, "Courier New", Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<h1>Introduction</h1>
Editing the xml configuration of an RCDC can be time consuming if you have a lot of custom objects and or attributes in the portal schema. This article describes a way to automate this task completely. It also provides a start in implementing this approach in powershell using the System.Xml.Linq library.
<a name='more'></a>
<h1>Creating a new rcdc</h1>
<p>
If you create a new object type in the portal schema, you want the end users to have a nice rcdc instead of the default interface. The portal offers you the ability to download a template file that you can then edit. If you strip this template file further, only retaining fields for display name and description, it is possible to reuse this as a starting point for all future rcdc's by editing the caption. A function could read such a local copy of the stripped down template, edit the caption and return the result.
</p>
<p>Implementation: <code><a href="https://github.com/wim-beck/IS4U-FIM-Powershell/blob/master/IS4U.FimPortal.Rcdc/IS4U.FimPortal.Rcdc.psm1#L208" target="_blank">Get-DefaultRcdc</a></code>.
</p>
<p>
The creation of a new rcdc requires following parameters:
<ul>
<li>Display Name</li>
<li>Target Object Type</li>
<li>XML Configuration Data</li>
<li>One of the following:
<ul>
<li>Applies to Create</li>
<li>Applies to Edit</li>
<li>Applies to View</li>
</ul></li>
</ul>
Creation of a new rcdc for the custom object Department could look like this:
<!-- code formatted by http://manoli.net/csharpformat/ -->
<pre class="csharpcode">
$departmentRcdc = Get-DefaultRcdc -Caption <span class="str">"Create Department"</span> -Create
New-Rcdc -DisplayName <span class="str">"Configuration for Department Creation"</span>
-ObjectType <span class="str">"Department"</span> -ConfigurationData $departmentRcdc -AppliesToCreate</pre>
</p>
<p>
Implementation: <code><a href="https://github.com/wim-beck/IS4U-FIM-Powershell/blob/master/IS4U.FimPortal.Rcdc/IS4U.FimPortal.Rcdc.psm1#L50" target="_blank">New-Rcdc</a></code>.
</p>
<h1>Updating an existing rcdc</h1>
<p>
Updating the rcdc is a little bit more complex. Controls need to be constructed. A separate function for each type of control can be defined.
</p>
<p>
Implementation:
<ul>
<li><code><a href="https://github.com/wim-beck/IS4U-FIM-Powershell/blob/master/IS4U.FimPortal.Rcdc/IS4U.FimPortal.Rcdc.psm1#L250" target="_blank">Get-RcdcIdentityPicker</a></code></li>
<li><code><a href="https://github.com/wim-beck/IS4U-FIM-Powershell/blob/master/IS4U.FimPortal.Rcdc/IS4U.FimPortal.Rcdc.psm1#L344" target="_blank">Get-RcdcTextBox</a></code></li>
<li><code><a href="https://github.com/wim-beck/IS4U-FIM-Powershell/blob/master/IS4U.FimPortal.Rcdc/IS4U.FimPortal.Rcdc.psm1#L402" target="_blank">Get-RcdcCheckBox</a></code></li>
</ul>
</p>
<p>
After generating the xml configuration for a control it needs to be inserted into an existing xml structure. The function that is responsible for this step requires at least following inputs:
<ul>
<li>Display name of the rcdc.</li>
<li>Name of the grouping where the control needs to be inserted. If the grouping does not exist, a new one will be created.</li>
<li>Xml configuration for the new control.</li>
<li>Optional: Caption for the grouping if it does not yet exist.</li>
</ul>
Implementation: <code><a href="https://github.com/wim-beck/IS4U-FIM-Powershell/blob/master/IS4U.FimPortal.Rcdc/IS4U.FimPortal.Rcdc.psm1#L146" target="_blank">Add-ElementToRcdc</a></code>.
</p>
<h1>Validating the rcdc</h1>
<p>Validation steps can be added before uploading xml configuration data, preventing lots of frustrated hours of searching for that one typo. Validation can also be done against manually edited rcdc configuration files and uses a local copy of the xsd schema file from technet. </p>
<p>Implementation: <code><a href="https://github.com/wim-beck/IS4U-FIM-Powershell/blob/master/IS4U.FimPortal.Rcdc/IS4U.FimPortal.Rcdc.psm1#L23" target="_blank">Test-RcdcConfiguration</a></code>.</p>
<h1>Todo</h1>
A lot of work remains. An inverse function for <code>Add-ElementToRcdc</code>, <code>Remove-ElementFromRcdc</code> needs to be implemented. Also, functions to generate other types of rcdc controls are lacking. Anyone can contribute! If you feel like writing a function or two, just go at it and send a pull request on the github project.
<h1>References</h1>
<ul>
<li><a href="https://fimpowershellmodule.codeplex.com/" target="_blank">FIM Powershell Module</a></li>
<li><a href="https://technet.microsoft.com/en-us/library/ff394179.aspx" target="_blank">FIM Cmdlets: FIM Automation</a></li>
<li><a href="https://espace.cern.ch/idm/Lists/Posts/Post.aspx?ID=25" target="_blank">Installing FIMAutomation on a FIM-less machine</a></li>
<li><a href="https://technet.microsoft.com/en-us/library/ee534918(v=ws.10).aspx#appendix_a" target="_blank">Technet: RCDC XSD Schema</a></li>
<li><a href="https://github.com/wim-beck/IS4U-FIM-Powershell/tree/master/IS4U.FimPortal.Rcdc" target="_blank">IS4U.FimPortal.Rcdc Module</a></li>
</ul>Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-37825983427065751442016-08-30T11:09:00.000+02:002016-08-30T11:13:08.297+02:00MIM2016 Troubleshooting: MIM Portal Performance Issue<h1>Issue</h1>
After experiencing a decrease in MIM portal responsiveness after installation, I checked the server resources to see following memory consumption:
<a href="http://wimbeck.be/wp-content/uploads/2016/03/RAM.png" rel="attachment wp-att-424"><img class="aligncenter size-full wp-image-424" src="http://wimbeck.be/wp-content/uploads/2016/03/RAM.png" alt="task manager" width="559" height="214" /></a>
<a name='more'></a>
<h1>Solution</h1>
The solution to this problem is quite simple. Since MIM is not using any search capabilities of the underlying Sharepoint engine, we can just remove the search component. You can do this either via the Central Administration or via Powershell.
<!-- code formatted by http://manoli.net/csharpformat/ -->
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, "Courier New", Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<pre class="csharpcode">
$spapp = Get-SPServiceApplication -Name <span class="str">"Search Service Application"</span>
Remove-SPServiceApplication $spapp -RemoveData</pre>
<a href="http://wimbeck.be/wp-content/uploads/2016/03/SearchApp.png" rel="attachment wp-att-425"><img class="aligncenter size-full wp-image-425" src="http://wimbeck.be/wp-content/uploads/2016/03/SearchApp.png" alt="Remove search application" width="580" /></a>
<h1>Related resources</h1>
<ul>
<li><a href="https://docs.microsoft.com/en-us/microsoft-identity-manager/deploy-use/prepare-server-sharepoint">Deploy Sharepoint Foundation</a></li>
<li><a href="https://blog.css-security.com/blog/optimizing-fim-performance">Other performance recommendations</a></li>
</ul>Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-61749935129641797442016-02-26T16:18:00.002+01:002016-02-26T16:48:19.729+01:00FIM2010: Selective Import Attribute Flow<h1>
Intro</h1>
Although FIM allows you to filter objects on import in the connector configuration, it does not allow you to filter attribute values for a certain population. An example use case could be: "Import all mobile numbers from HR, but not the mobile numbers for person X, Y and Z". Reason for this could be that the X, Y and Z's numbers should not occur in the global address list or some other system where they can be viewed by others.<br />
<br />
This problem can be solved by manipulating the import attribute flow. Simply block the value of the mobile number for these people to flow to the metaverse. To complete this solution, make sure "allow null on export" is enabled on all export attribute flows for mobile. Also, if it is possible for people to update their mobile number in data sources other than HR, make sure there is an import attribute flow from these sources to prevent data loss.
Alternatively, it can be solved by manipulating all export attribute flows for mobile numbers. Because this would have an impact on possible future export attribute flows for mobile, the import approach seems the way to go.
<br />
<a name='more'></a><h1>
Possible solutions</h1>
<ol>
<li>Use two synchronization rules instead of one: one with the scope of people flowing mobile, one with the scope of people not flowing mobile.</li>
<li>advanced attribute flow
<ul>
<li>Based on a list, external to the data source and FIM. Con of this option is that the list needs to be maintained.</li>
<li>Based on a flag in the data source. This solution would need a flag for each attribute you need to filter on import.</li>
</ul>
</li>
</ol>
Because two sync rules seemed too complex for such a simple task, I choose to implement it using an advanced import rule and since no flag was available in the data source, the solution is based on a list.
<br />
<h1>
Solution using Rules Extension</h1>
All accounts are listed in an xml file, using their anchor value of the data source where the mobile number is originating from. In the case of an HR system, this could be the database key.<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ -->
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, "Courier New", Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<div class="csharpcode">
<pre class="alt"><Filter></pre>
<pre> <User>007</User></pre>
<pre class="alt"></Filter></pre>
</div>
<br />
The xml file is read during the initialize phase of the Rules Extension. First, the installation directory location is read from the registry. After that, the file is read and the list is constructed using Linq. The sample code contains constant strings for clarity. Best practice would be to remove these values and put them in a configuration file and read the values from there.<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ -->
<br />
<div class="csharpcode">
<pre class="alt"><span class="kwrd">using</span> Microsoft.MetadirectoryServices;</pre>
<pre><span class="kwrd">using</span> Microsoft.Win32;</pre>
<pre class="alt"><span class="kwrd">using</span> System;</pre>
<pre><span class="kwrd">using</span> System.Collections.Generic;</pre>
<pre class="alt"><span class="kwrd">using</span> System.IO;</pre>
<pre><span class="kwrd">using</span> System.Linq;</pre>
<pre class="alt"><span class="kwrd">using</span> System.Xml.Linq;</pre>
<pre> </pre>
<pre class="alt"><span class="kwrd">private</span> <span class="kwrd">const</span> <span class="kwrd">string</span> CONFDIR = <span class="str">"Extensions"</span>;</pre>
<pre><span class="kwrd">private</span> <span class="kwrd">const</span> <span class="kwrd">string</span> FIM_SUBKEY = <span class="str">@"SYSTEM\CurrentControlSet\Services\FIMSynchronizationService\Parameters"</span>;</pre>
<pre class="alt"><span class="kwrd">private</span> <span class="kwrd">const</span> <span class="kwrd">string</span> FIM_SUBKEY_VAR = <span class="str">"Path"</span>;</pre>
<pre><span class="kwrd">private</span> <span class="kwrd">const</span> <span class="kwrd">string</span> filterFile = <span class="str">"mobileFilter.xml"</span>;</pre>
<pre class="alt"><span class="kwrd">private</span> List<<span class="kwrd">string</span>> mobileFilter;</pre>
<pre> </pre>
<pre class="alt"><span class="rem">/// <summary></span></pre>
<pre><span class="rem">/// FIM Extensions folder path. (Microsoft Forefront Identity Manager\2010\Synchronization Service\Extensions)</span></pre>
<pre class="alt"><span class="rem">/// </summary></span></pre>
<pre><span class="kwrd">public</span> <span class="kwrd">string</span> SourceDirectory</pre>
<pre class="alt">{</pre>
<pre> <span class="kwrd">get</span></pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">string</span> sourceDirectory = <span class="kwrd">string</span>.Empty;</pre>
<pre class="alt"> <span class="kwrd">using</span> (RegistryKey key = Registry.LocalMachine.OpenSubKey(FIM_SUBKEY, <span class="kwrd">false</span>))</pre>
<pre> {</pre>
<pre class="alt"> <span class="kwrd">if</span> (key != <span class="kwrd">null</span>)</pre>
<pre> {</pre>
<pre class="alt"> sourceDirectory = key.GetValue(FIM_SUBKEY_VAR).ToString();</pre>
<pre> }</pre>
<pre class="alt"> }</pre>
<pre> <span class="kwrd">if</span> (<span class="kwrd">string</span>.IsNullOrEmpty(sourceDirectory))</pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">throw</span> <span class="kwrd">new</span> Exception(<span class="str">"Error while reading registry"</span>);</pre>
<pre class="alt"> }</pre>
<pre> <span class="kwrd">return</span> Path.Combine(sourceDirectory, CONFDIR);</pre>
<pre class="alt"> }</pre>
<pre>}</pre>
<pre class="alt"> </pre>
<pre><span class="rem">/// <summary></span></pre>
<pre class="alt"><span class="rem">/// Initialization of the rules extension object.</span></pre>
<pre><span class="rem">/// </summary></span></pre>
<pre class="alt"><span class="kwrd">void</span> IMASynchronization.Initialize() </pre>
<pre>{</pre>
<pre class="alt"> XDocument mobiles = XDocument.Load(Path.Combine(SourceDirectory, filterFile));</pre>
<pre> mobileFilter = mobiles.Root.Elements(<span class="str">"User"</span>).Select(user => user.Value).ToList();</pre>
<pre class="alt">}</pre>
</div>
<br />
The import rule is rather simple, now that everything is in place. Just check whether the entry that is about to be imported occurs in the list and make the proper decision: either import the value into the metaverse or delete it.<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ -->
<br />
<div class="csharpcode">
<pre class="alt"><span class="rem">/// <summary></span></pre>
<pre><span class="rem">/// Import mobile number if not on the list of numbers to filter.</span></pre>
<pre class="alt"><span class="rem">/// </summary></span></pre>
<pre><span class="rem">/// <param name="mventry">Destination metaverse entry.</param></span></pre>
<pre class="alt"><span class="rem">/// <param name="csentry">Source connector space entry.</param></span></pre>
<pre><span class="kwrd">private</span> <span class="kwrd">void</span> importMobile(MVEntry mventry, CSEntry csentry)</pre>
<pre class="alt">{</pre>
<pre> <span class="kwrd">if</span> (csentry[<span class="str">"mobile"</span>].IsPresent && !mobileFilter.Contains(csentry.DN.ToString()))</pre>
<pre class="alt"> {</pre>
<pre> mventry[<span class="str">"mobile"</span>].Value = csentry[<span class="str">"mobile"</span>].Value;</pre>
<pre class="alt"> }</pre>
<pre> <span class="kwrd">else</span></pre>
<pre class="alt"> {</pre>
<pre> mventry[<span class="str">"mobile"</span>].Delete();</pre>
<pre class="alt"> }</pre>
<pre>}</pre>
</div>
<h1>
See Also</h1>
<a href="http://blog.is4u.be/2015/07/fim2010-writing-advanced-attribute-flows.html" target="_blank">FIM2010: Writing Advanced Attribute Flows</a>
Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-6575912132951060672015-12-31T16:41:00.000+01:002015-12-31T16:42:32.923+01:00FIM2010: Outbound System Scoping Filter Syntax<h1>Intro</h1>
FIM 2010 R2 was the first version that brings outbound system scoping filters into the synchronization rule. I decided to use this feature during a migration from FIM 2010 to eliminate some worfklows, sets and policy rules. This would reduce complexity and make the synchronization configuration more comprehensible.
<a href="http://wimbeck.be/wp-content/uploads/2016/01/scopingFilter.png" rel="attachment wp-att-376"><img src="http://wimbeck.be/wp-content/uploads/2016/01/scopingFilter.png" alt="scopingFilter" width="581" class="aligncenter size-full wp-image-376" /></a>
Instead of searching for the workflow that adds the sync rule, the MPR that triggers the workflow and then going to the set definition of the target population of the MPR, you can just examine the outbound scoping filter of the sync rule itself.
<a name='more'></a>
<h1>Methodology</h1>
I followed following flow:
<ol>
<li>Check if set membership is a logical AND of conditions</li>
<li>if yes: configure set conditions on outbound scoping filter of the synchronization rule and delete the wf-set-mpr triplet</li>
<li>If not: leave config as is</li>
</ol>
<h1>Problem</h1>
While this methodology proved to be effect for most of my sync rules, one was causing problems. It was not being applied to the entries it should have been. I transfered the original set condition to the sync rule:
<a href="http://wimbeck.be/wp-content/uploads/2016/01/scopingFilter2.png" rel="attachment wp-att-379"><img src="http://wimbeck.be/wp-content/uploads/2016/01/scopingFilter2.png" alt="scopingFilter2" width="581" class="aligncenter size-full wp-image-379" /></a>
But the sync rule status was "not applied".
<a href="http://wimbeck.be/wp-content/uploads/2016/01/scopingFilter3.png" rel="attachment wp-att-380"><img src="http://wimbeck.be/wp-content/uploads/2016/01/scopingFilter3.png" alt="scopingFilter3" width="304" height="143" class="aligncenter size-full wp-image-380" /></a>
<h1>Solution</h1>
After some googling, I found the origin of my problem in a blog post. The syntax of the outbound system scoping filter is different from the syntax used in set conditions.
The correct way to configure this is by using "not-equal" the empty string:
<a href="http://wimbeck.be/wp-content/uploads/2016/01/scopingFilter4.png" rel="attachment wp-att-382"><img src="http://wimbeck.be/wp-content/uploads/2016/01/scopingFilter4.png" alt="scopingFilter4" width="581" class="aligncenter size-full wp-image-382" /></a>
After submitting this change, you need to manually correct the syntax, because it will throw an error if you try to import it into the metaverse. Open the sync rule once again, go to the Advanced View and into the Extended Attributes tab:
<a href="http://wimbeck.be/wp-content/uploads/2016/01/scopingFilter5.png" rel="attachment wp-att-383"><img src="http://wimbeck.be/wp-content/uploads/2016/01/scopingFilter5.png" alt="scopingFilter5" width="581" class="aligncenter size-full wp-image-383" /></a>
Edit the scoping filter attribute by converting
<!-- code formatted by http://manoli.net/csharpformat/ -->
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, "Courier New", Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<pre class="csharpcode">
<span class="kwrd"><</span><span class="html">scoping</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">scope</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">csAttribute</span><span class="kwrd">></span>accountName<span class="kwrd"></</span><span class="html">csAttribute</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">csOperator</span><span class="kwrd">></span>NOTEQUAL<span class="kwrd"></</span><span class="html">csOperator</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">csValue</span><span class="kwrd">></</span><span class="html">csValue</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">scope</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">scoping</span><span class="kwrd">></span></pre>
into
<!-- code formatted by http://manoli.net/csharpformat/ -->
<pre class="csharpcode">
<span class="kwrd"><</span><span class="html">scoping</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">scope</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">csAttribute</span><span class="kwrd">></span>accountName<span class="kwrd"></</span><span class="html">csAttribute</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">csOperator</span><span class="kwrd">></span>NOTEQUAL<span class="kwrd"></</span><span class="html">csOperator</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">csValue</span><span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">scope</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">scoping</span><span class="kwrd">></span></pre>
<h1>Resources</h1>
<ul>
<li><a href="http://c--shark.blogspot.be/2013/07/outbound-system-scoping-filter-contains.html" target="_blank">Outbound System Scoping Filter: "Contains" doesn't work on multi-valued attributes (and why no "Is Present"?)</a></li>
</ul>Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-14978082506358739072015-09-30T21:44:00.005+02:002015-09-30T21:59:58.301+02:00FIM2010: Localize Self Service Password Reset<h1>
Intro</h1>
If you ever had the pleasure of installing one or multiple language packs in FIM, you know that not everything is covered. The question and answer gate is a good example and is very visible to the end user. This blog post discusses how you can localize such a configuration and shows how it is possible to automate this task using powershell.<br />
<a name='more'></a>
<h1>
SSPR</h1>
Self service password reset consists of the following configuration blocks:
<br />
<ul>
<li>A workflow: Password Reset AuthN Workflow</li>
<li>A management policy rule: Anonymous users can reset their password</li>
<li>A set: Password Reset Users Set</li>
</ul>
The workflow defines which actions are necessary before a user is allowed to reset her password. This can be an otp email, sms or a question and answer gate.
The management policy rule links the users that are allowed to reset their password to the correct authentication workflow.
The set is linked to the MPR as the target resource field, because the requestor of a password reset authentication workflow is always an anonymous user.
<br />
<h1>
Localize</h1>
The language packs that come with FIM do not include localized configuration objects. So, we have to configure these ourselves. Once you know how the mechanism works, it is pretty simple. You duplicate the three configuration items that are required to have a working SSPR configuration for each language you want to support and make sure you define the correct population in the MPR target set. Then you disable the default MPR to avoid confusion. A sample configuration could look like this:<br />
<a href="http://wimbeck.be/wp-content/uploads/2015/09/sspr_mpr.png"><img alt="sspr_mpr" class="aligncenter size-full wp-image-350" src="http://wimbeck.be/wp-content/uploads/2015/09/sspr_mpr.png" height="348" width="400" /></a>
<a href="http://wimbeck.be/wp-content/uploads/2015/09/sspr_mpr2.png"><img alt="sspr_mpr2" class="aligncenter size-full wp-image-351" src="http://wimbeck.be/wp-content/uploads/2015/09/sspr_mpr2.png" height="350" width="400" /></a><br />
<br />
You make sure that the authentication workflow uses the language corresponding to the target population. It is also recommended to configure one of the languages as default for the user accounts that do not have a proper language configured. This can be configured as follows:<br />
<br />
<br />
<a href="http://wimbeck.be/wp-content/uploads/2015/09/sspr_set.png"><img alt="sspr_set" class="aligncenter size-full wp-image-353" src="http://wimbeck.be/wp-content/uploads/2015/09/sspr_set.png" height="310" width="440" /></a><br />
<br />
As last step, you need to add these new objects to the <code>Password Reset Objects Set</code>. Otherwise SSPR will not work.
<br />
<h1>
Powershell</h1>
Of course it is a lot of work to localize SSPR if you have more than two languages. That is why we wrote a powershell function to do this for us. It is also very handy to update an existing configuration. So if you want to add or change a secret question, powershell is a lot easier and faster than doing this job manually. Sample code can be found at <a href="https://github.com/wim-beck/IS4U-FIM-Powershell" target="_blank">github</a>. The module <a href="https://github.com/wim-beck/IS4U-FIM-Powershell/blob/master/Is4uFimSspr.psm1" target="_blank">Is4uFimSspr.psm1</a> provides following functions:
<br />
<ul>
<li>Enable-Sspr</li>
<li>Disable-Sspr</li>
<li>Install-LocalizedSspr</li>
</ul>
The first two enable and disable builtin MPR's for SSPR. The last one localizes the default question and answer gate based on information provided in an XML configuration file: <a href="https://github.com/wim-beck/IS4U-FIM-Powershell/blob/master/sspr.xml" target="_blank">sspr.xml</a>. The function assumes that the default configuration of <code>Password Reset AuthN Workflow</code> is untouched. It copies the XOML field and does a string replace of the questions, constraints and error messages with the values from the configuration file.
<br />
<h1>
Resources</h1>
<ul>
<li><a href="https://technet.microsoft.com/en-us/library/jj134308%28v=ws.10%29.aspx" target="_blank">Deploying FIM SSPR</a></li>
<li><a href="https://fimpowershellmodule.codeplex.com/" target="_blank">FIM Powershell module</a></li>
<li><a href="https://technet.microsoft.com/en-us/library/ff394179.aspx" target="_blank">FIM Automation snapin</a></li>
<li><a href="https://github.com/wim-beck/IS4U-FIM-Powershell" target="_blank">IS4U FIM Powershell modules</a></li>
</ul>
Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-34524767861510882492015-07-17T15:36:00.002+02:002015-09-30T21:45:06.921+02:00FIM2010: Writing Advanced Attribute Flows<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, "Courier New", Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<h1>Intro</h1>
Once in a while you will come across very complex business requirements while implementing FIM in a large environment. These requirements often require a classic architecture (with VB or C# extensions), but can create very messy code that is hard to maintain.
This article does not start another discussion on whether or not you should (try to) use 100% declarative (codeless) or a classic architecture when implementing such large scenarios. A good article on this topic: <a href="http://blog.kloud.com.au/2013/10/04/fim-case-study-trying-to-achieve-a-100-declarative-or-codeless-architecture/" target="_blank">codeless architecture and when you are not able to use declarative configuration</a>. Instead, this article will focus on how you should implement a proper classic architecture, in a way that is performant, readable, agile and easy to maintain.
<a name='more'></a>
<h1>Advanced flow rules</h1>
A basic map attributes for import method is written as follows:
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt"><span class="kwrd">void</span> IMASynchronization.MapAttributesForImport(</pre>
<pre> <span class="kwrd">string</span> flowRuleName, CSEntry csentry, MVEntry mventry)</pre>
<pre class="alt">{</pre>
<pre> <span class="kwrd">switch</span> (flowRuleName)</pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">case</span> <span class="str">"SomeFlowRuleName"</span>:</pre>
<pre class="alt"> {</pre>
<pre> <span class="rem">// Some code ...</span></pre>
<pre class="alt"> }</pre>
<pre> <span class="kwrd">break</span>;</pre>
<pre class="alt"> <span class="kwrd">default</span>:</pre>
<pre> {</pre>
<pre class="alt"> <span class="kwrd">throw</span> <span class="kwrd">new</span> EntryPointNotImplementedException(</pre>
<pre> <span class="kwrd">string</span>.Concat(<span class="str">"Flow rule name not found: "</span>, </pre>
<pre class="alt"> flowRuleName));</pre>
<pre> }</pre>
<pre class="alt"> }</pre>
<pre>}</pre>
</div>
Imagine you have 12 advanced import rules, which is not that many in a big environment. A rule has an average of 16 lines of code. Because you have already 4 lines of code for each case statement, you get a method of 240 lines.
You could create a method for each case statement. That way the switch statement becomes more readable. What if we want to take this one step further? That is where reflection comes into place.
<h1>Reflection</h1>
Reflection is a programming concept that enables you to inspect (or even change) source code. You can apply reflection on a class itself, but also on other classes. Reflection enables you to use the same implementation of the method <code>MapAttributesForImport</code> in all your rules extensions. Similar code can also be used for <code>MapAttributesForExport</code>, <code>MapAttributesForJoin</code> and <code>ResolveJoinSearch</code>.
Each attribute flow is implemented in its own method, eg <code>importMustChangePassword</code>. By using a simple naming convention, import/export target attribute name, readability is very good. Because each attribute can only be target of one export attribute flow, this convention also ensures uniqueness.
<h2>Code using reflection</h2>
<p>
First line constructs an array of objects. These are the parameters that will be passed to our advanced attribute flow method. Second line constructs a <code>BindingFlags</code> object. This object determines what kind of method we will call. The binding flags describes the method signature. <code>InvokeMethod</code> indicates we will not call a constructor. <code>NonPublic</code> indicates the access modifier: we will call a private, internal or protected method. <code>Instance</code> specifies that instance members are to be included in the search.
</p>
<p>
The third line does the actual call. It will invoke the method on the current class. The reflection mechanism will search in the current class for methods matching the flowRuleName, bindingFlags and the number and type of parameters. If one and only one match is found, the method is invoked.
</p>
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt"><span class="kwrd">void</span> IMASynchronization.MapAttributesForImport(<span class="kwrd">string</span> flowRuleName, </pre>
<pre> CSEntry csentry, MVEntry mventry)</pre>
<pre class="alt">{</pre>
<pre> <span class="kwrd">try</span></pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">object</span>[] parameters = { mventry, csentry };</pre>
<pre class="alt"> BindingFlags bindingFlags = BindingFlags.InvokeMethod | </pre>
<pre> BindingFlags.NonPublic | BindingFlags.Instance;</pre>
<pre class="alt"> <span class="kwrd">this</span>.GetType().InvokeMember(flowRuleName, bindingFlags, </pre>
<pre> <span class="kwrd">null</span>, <span class="kwrd">this</span>, parameters);</pre>
<pre class="alt"> }</pre>
<pre> <span class="kwrd">catch</span> (MissingMethodException)</pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">throw</span> <span class="kwrd">new</span> EntryPointNotImplementedException(</pre>
<pre class="alt"> <span class="kwrd">string</span>.Concat(<span class="str">"Flow rule name not found: "</span>, </pre>
<pre> flowRuleName));</pre>
<pre class="alt"> }</pre>
<pre>}</pre>
</div>
<h2>Example attribure flow rule</h2>
An example to illustrate the mechanism is this rule to set a flag on the metaverse person object whether he has to change his password.
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt"><span class="rem">/// Imports the must change password flag. </span></pre>
<pre><span class="rem">/// This flag is true if the pwdLastSet timestamp</span></pre>
<pre class="alt"><span class="rem">/// attribute is set to 0.</span></pre>
<pre><span class="rem">/// </summary></span></pre>
<pre class="alt"><span class="rem">/// <param name="mventry">Destination metaverse entry.</param></span></pre>
<pre><span class="rem">/// <param name="csentry">Source connector space entry.</param></span></pre>
<pre class="alt"><span class="kwrd">private</span> <span class="kwrd">void</span> importMustChangePassword(MVEntry mventry, CSEntry csentry)</pre>
<pre>{</pre>
<pre class="alt"> <span class="kwrd">if</span> (csentry[<span class="str">"pwdLastSet"</span>].IsPresent)</pre>
<pre> {</pre>
<pre class="alt"> mventry[<span class="str">"mustChangePassword"</span>].BooleanValue = </pre>
<pre> csentry[<span class="str">"pwdLastSet"</span>].IntegerValue == 0;</pre>
<pre class="alt"> }</pre>
<pre>}</pre>
</div>
<h1>References</h1>
<ul>
<li><a href="https://technet.microsoft.com/en-us/library/jj590309%28v=ws.10%29.aspx" target="_blank">Rules Extensions</a></li>
<li><a href="http://www.wapshere.com/missmiis/advanced-attribute-flow-rules" target="_blank">Advanced flow rules on Miss MIIS</a></li>
<li><a href="https://msdn.microsoft.com/en-us/library/ms173183.aspx" target="_blank">Reflection</a></li>
<li><a href="https://msdn.microsoft.com/en-us/library/system.reflection.bindingflags%28v=vs.110%29.aspx" target="_blank">BindingFlags Enumeration</a></li>
</ul>Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-75495056523309700182015-03-17T11:10:00.000+01:002015-03-17T14:38:07.882+01:00Visual C#: RSA encryption using certificate<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, "Courier New", Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<h2>Intro</h2>
RSA is a well-known cryptosystem using assymetric encryption. It performs encryption using a public key, decryption using a private key. The private key should be protected. The most efficient way of managing these keys in a Windows environment is by using certificates. To protect the private key, you should make it not exportable. This way the private key is only available on the machine it is being used.
<a name='more'></a>
<h2>Create certificate</h2>
<h3>Request certificate from CA</h3>
When enrolling for a certificate, make sure that the template has the Legacy Cryptographic Service Provider selected. Otherwise .Net will not be able to use the certificate. It will crash with this exception:
<blockquote>Unhandled Exception: System.Security.Cryptography.CryptographicException: Invalid provider type specified.</blockquote>
<br />
<a href="https://wimbeck.be/wp-content/uploads/2015/03/rsa_template.png"><img src="https://wimbeck.be/wp-content/uploads/2015/03/rsa_template.png" alt="rsa_template" width="380" /></a>
<h3>Generate self-signed certifiate</h3>
Windows Server 2012 R2 provides a cmdlet, <code>New-SelfSignedCertificate</code>, to generate a certificate. However, this cmdlet does not provide sufficient parameters to generate a certificate that can be used in C#. I used following <a href="https://gallery.technet.microsoft.com/scriptcenter/Self-signed-certificate-5920a7c6#content">script</a> to generate my self-signed certificate, <code>New-SelfsignedCertificateEx</code>:
<blockquote>This script is an enhanced open-source PowerShell implementation of deprecated makecert.exe tool and utilizes the most modern certificate API — CertEnroll.</blockquote>
In my search for the correct value for the ProviderName parameter, I came across the interface of <code>IX509PrivateKey</code>, which provides a LegacyCsp boolean flag. I added following <code>$PrivateKey.LegacyCsp = $true</code> in <code>#region Private Key</code> [line 327]. After this addition, following powershell command gave me a certificate I could use to perform RSA encryption and decryption:
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt">param($certName)</pre>
<pre>Import-Module .\New-SelfSignedCertificateEx.psm1</pre>
<pre class="alt">New-SelfsignedCertificateEx -Subject <span class="str">"CN=$certName"</span> </pre>
<pre> -KeyUsage <span class="str">"KeyEncipherment, DigitalSignature"</span> </pre>
<pre class="alt"> -StoreLocation <span class="str">"LocalMachine"</span> -KeyLength 4096</pre>
</div>
<h3>Grant access to private key</h3>
The account(s) that will perform the decryption requires read access to the private key of the certificate. To configure this, open a management console (mmc). Add the certificates snap-in for the local computer. In the certificate store, right click the certificate, go to all tasks and click Manage Private Keys. Add the account and select Read. Apply the changes.
<br />
<a href="https://wimbeck.be/wp-content/uploads/2015/03/rsa_privKey.png"><img class="aligncenter size-full wp-image-260" src="https://wimbeck.be/wp-content/uploads/2015/03/rsa_privKey.png" alt="rsa_privKey" width="368" height="449" /></a>
<br />
Alternatively, you can script the process using an <a href="http://poshcode.org/3637">extra module</a> to find the private key location:
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt">param($certName, $user)</pre>
<pre>Import-Module .\Get-PrivateKeyPath.psm1</pre>
<pre class="alt">$privateKeyPath = Get-PrivateKeyPath CN=$certName -StoreName My </pre>
<pre> -StoreScope LocalMachine</pre>
<pre class="alt">& icacls.exe $privateKeyPath /grant (<span class="str">"{0}:R"</span> -f $user)</pre>
</div>
<h3>Export public key</h3>
The certificate with public key can be published and/or transported to a partner that needs to send sensitive data. To export the certificate with public key execute following script:
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt">param($certName)</pre>
<pre>$Thumbprint = (Get-ChildItem -Path Cert:\LocalMachine\My </pre>
<pre class="alt"> | Where-Object {$_.Subject -match <span class="str">"CN=$certName"</span>}).Thumbprint;</pre>
<pre>Export-Certificate -FilePath <span class="str">"C:\certName.crt"</span> </pre>
<pre class="alt"> -Cert Cert:\LocalMachine\My\$Thumbprint</pre>
<pre> </pre>
</div>
Do not forget to refresh the certificate keys on a regular basis.
<h2>Load certificate</h2>
Following code snippet shows how to locate and load the certificate.
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt"><span class="kwrd">using</span> System;</pre>
<pre><span class="kwrd">using</span> System.Security.Cryptography.X509Certificates;</pre>
<pre class="alt"> </pre>
<pre><span class="kwrd">private</span> X509Certificate2 getCertificate(<span class="kwrd">string</span> certificateName)</pre>
<pre class="alt">{</pre>
<pre> X509Store my = <span class="kwrd">new</span> X509Store(StoreName.My, StoreLocation.LocalMachine);</pre>
<pre class="alt"> my.Open(OpenFlags.ReadOnly);</pre>
<pre> X509Certificate2Collection collection = </pre>
<pre class="alt"> my.Certificates.Find(X509FindType.FindBySubjectName, certificateName, <span class="kwrd">false</span>);</pre>
<pre> <span class="kwrd">if</span> (collection.Count == 1)</pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">return</span> collection[0];</pre>
<pre class="alt"> }</pre>
<pre> <span class="kwrd">else</span> <span class="kwrd">if</span> (collection.Count > 1)</pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">throw</span> <span class="kwrd">new</span> Exception(<span class="kwrd">string</span>.Format(<span class="str">"More than one certificate with name</pre>
<pre class="alt"> '{0}' found in store LocalMachine/My."</span>, certificateName));</pre>
<pre> }</pre>
<pre class="alt"> <span class="kwrd">else</span></pre>
<pre> {</pre>
<pre class="alt"> <span class="kwrd">throw</span> <span class="kwrd">new</span> Exception(<span class="kwrd">string</span>.Format(<span class="str">"Certificate '{0}' not found </pre>
<pre> in store LocalMachine/My."</span>, certificateName));</pre>
<pre class="alt"> }</pre>
<pre>}</pre>
</div>
<h2>Encryption</h2>
Following code snippet shows how to encrypt the input using a certificate.
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt"><span class="kwrd">using</span> System;</pre>
<pre><span class="kwrd">using</span> System.Security.Cryptography;</pre>
<pre class="alt"><span class="kwrd">using</span> System.Security.Cryptography.X509Certificates;</pre>
<pre><span class="kwrd">using</span> System.Text;</pre>
<pre class="alt"> </pre>
<pre><span class="kwrd">private</span> <span class="kwrd">string</span> EncryptRsa(<span class="kwrd">string</span> input)</pre>
<pre class="alt">{</pre>
<pre> <span class="kwrd">string</span> output = <span class="kwrd">string</span>.Empty;</pre>
<pre class="alt"> X509Certificate2 cert = getCertificate(certificateName);</pre>
<pre> <span class="kwrd">using</span> (RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key)</pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">byte</span>[] bytesData = Encoding.UTF8.GetBytes(input);</pre>
<pre class="alt"> <span class="kwrd">byte</span>[] bytesEncrypted = csp.Encrypt(bytesData, <span class="kwrd">false</span>);</pre>
<pre> output = Convert.ToBase64String(bytesEncrypted);</pre>
<pre class="alt"> }</pre>
<pre> <span class="kwrd">return</span> output;</pre>
<pre class="alt">}</pre>
</div>
<h2>Decryption</h2>
Following code snippet shows how to decrypt the input using a certificate. Make sure that the account running this has read access to the private key.
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt"><span class="kwrd">using</span> System;</pre>
<pre><span class="kwrd">using</span> System.Security.Cryptography;</pre>
<pre class="alt"><span class="kwrd">using</span> System.Security.Cryptography.X509Certificates;</pre>
<pre><span class="kwrd">using</span> System.Text;</pre>
<pre class="alt"> </pre>
<pre><span class="kwrd">private</span> <span class="kwrd">string</span> decryptRsa(<span class="kwrd">string</span> encrypted)</pre>
<pre class="alt">{</pre>
<pre> <span class="kwrd">string</span> text = <span class="kwrd">string</span>.Empty;</pre>
<pre class="alt"> X509Certificate2 cert = getCertificate(certificateName);</pre>
<pre> <span class="kwrd">using</span> (RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PrivateKey)</pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">byte</span>[] bytesEncrypted = Convert.FromBase64String(encrypted);</pre>
<pre class="alt"> <span class="kwrd">byte</span>[] bytesDecrypted = csp.Decrypt(bytesEncrypted, <span class="kwrd">false</span>);</pre>
<pre> text = Encoding.UTF8.GetString(bytesDecrypted);</pre>
<pre class="alt"> }</pre>
<pre> <span class="kwrd">return</span> text;</pre>
<pre class="alt">}</pre>
</div>
<h2>References</h2>
<ul>
<li><a href="https://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider%28v=vs.110%29.aspx">RSACryptoServiceProvider</a></li>
<li><a href="https://technet.microsoft.com/en-us/library/hh848633.aspx">New-SelfSignedCertificate</a></li>
<li><a href="https://gallery.technet.microsoft.com/scriptcenter/Self-signed-certificate-5920a7c6">Self-signed certificate generator (PowerShell)</a></li>
<li><a href="http://paulstovell.com/blog/x509certificate2">Eight tips for working with X.509 certificates in .NET</a></li>
<li><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa378921%28v=vs.85%29.aspx">IX509PrivateKey interface</a></li>
<li><a href="http://poshcode.org/3637">Get-PrivateKeyPath</a></li>
</ul>Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-3019315649712154492015-03-13T15:01:00.000+01:002015-03-13T16:01:19.306+01:00FIM2010: Protect passwords in configuration files<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, "Courier New", Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<h2>Intro</h2>
One of the great features of FIM is that it is relatively easy to plugin custom functionality. You can extend the synchronization engine by developing rules extension and you can add custom workflows to the FIM portal. Rules extensions run under the FIM synchronization service account, workflows under the FIM service service account. This article describes an approach to enable communication to external systems (eg Exchange). Because you typically do not grant a service account rights to Microsoft Exchange, you need the ability to run part of your code using different credentials.
<h2>Encrypt password</h2>
You do not want to have passwords in clear text in configuration files or source code. That is where encryption comes into play. Encryption can be handled in a myriad of different ways. The method described here uses powershell cmdlets, which keeps it quite simple and understandable.
<a name='more'></a>
So, how do we convert plain text to something more secure that cannot be read by anyone who happens to have read access to the files? Following two powershell commands are the answer:
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt">$secureString = ConvertTo-SecureString -AsPlainText </pre>
<pre> -Force -String $pwd</pre>
<pre class="alt">$text = ConvertFrom-SecureString $secureString</pre>
</div>
The cmdlet <code>ConvertTo-SecureString</code> creates a secure string object from the password stored in the variable <code>$pwd</code>. The <code>$text</code> variable is a textual representation of the secure string and looks something like this:
01000000d08c9ddf0115d1118c7a00
c04fc297eb01000000c6c2de88df86
70438e7ac64054c971490000000002
000000000003660000c00000001000
0000103f2b826467c9fdaff555bc06
4b4da50000000004800000a0000000
1000000006673bf0a8cd2463ec9f9f
d1a911dfc30800000076d2a3f9c110
2b6e1e0000006452d271a75a5df3a6
00f0f7cb45c18df98d3aae
<h2>Decrypt password</h2>
<h3>PowerShell</h3>
To decrypt the password in powershell, use the <code>ConvertTo-SecureString</code> cmdlet:
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt">ConvertTo-SecureString $text</pre>
</div>
The output of this cmdlet is a secure string object which can be used to build a <code>PSCredential</code> object.
<h3>C#</h3>
To perform decryption in C# you need to add a reference to <code>System.Management.Automation</code> in your project. To export the dll to the current directory execute following powershell cmd:
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt">Copy ([PSObject].Assembly.Location) .</pre>
</div>
Following code shows how to use the PowerShell library to construct a PSCredential object. The PSCredential object can then be used to perform management operations on Exchange.
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt"><span class="kwrd">using</span> System.Collections.ObjectModel;</pre>
<pre><span class="kwrd">using</span> System.Management.Automation;</pre>
<pre class="alt"><span class="kwrd">using</span> System.Security;</pre>
<pre> </pre>
<pre class="alt"><span class="kwrd">private</span> <span class="kwrd">string</span> PWD = <span class="str">"01000000d08c9ddf0115d1118c7</pre>
<pre>a00c04fc297eb01000000c6c2de88df8670438e7ac64054c9</pre>
<pre class="alt">71490000000002000000000003660000c0000000100000001</pre>
<pre>03f2b826467c9fdaff555bc064b4da50000000004800000a0</pre>
<pre class="alt">0000001000000006673bf0a8cd2463ec9f9fd1a911dfc3080</pre>
<pre>0000076d2a3f9c1102b6e1e0000006452d271a75a5df3a600</pre>
<pre class="alt">f0f7cb45c18df98d3aae"</span>;</pre>
<pre><span class="kwrd">private</span> <span class="kwrd">string</span> USER = <span class="str">@"is4u\sa-ms-exch"</span>;</pre>
<pre class="alt"> </pre>
<pre><span class="kwrd">private</span> PSCredential getPowershellCredential()</pre>
<pre class="alt">{</pre>
<pre> PSCredential powershellCredential;</pre>
<pre class="alt"> <span class="kwrd">string</span> powershellUsername = USER;</pre>
<pre> SecureString pwd = getPowershellPassword(PWD);</pre>
<pre class="alt"> <span class="kwrd">if</span> (pwd != <span class="kwrd">null</span>)</pre>
<pre> {</pre>
<pre class="alt"> powershellCredential = <span class="kwrd">new</span> </pre>
<pre> PSCredential(powershellUsername, pwd);</pre>
<pre class="alt"> }</pre>
<pre> <span class="kwrd">else</span></pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">throw</span> <span class="kwrd">new</span> Exception(<span class="str">"Password is invalid"</span>);</pre>
<pre class="alt"> }</pre>
<pre> <span class="kwrd">return</span> powershellCredential;</pre>
<pre class="alt">}</pre>
<pre> </pre>
<pre class="alt"><span class="kwrd">private</span> SecureString getPowershellPassword(<span class="kwrd">string</span> encryptedPwd)</pre>
<pre>{</pre>
<pre class="alt"> SecureString pwd = <span class="kwrd">null</span>;</pre>
<pre> <span class="kwrd">using</span> (PowerShell powershell = PowerShell.Create())</pre>
<pre class="alt"> {</pre>
<pre> powershell.AddCommand("ConvertTo-SecureString");</pre>
<pre class="alt"> powershell.AddParameter("String", encryptedPwd);</pre>
<pre> Collection<PSObject> results = powershell.Invoke();</pre>
<pre class="alt"> <span class="kwrd">if</span> (results.Count > 0)</pre>
<pre> {</pre>
<pre class="alt"> PSObject result = results[0];</pre>
<pre> pwd = (SecureString)result.BaseObject;</pre>
<pre class="alt"> }</pre>
<pre> }</pre>
<pre class="alt"> <span class="kwrd">return</span> pwd;</pre>
<pre>}</pre>
</div>
<h3>NetworkCredential</h3>
If your operation requires you to connect using a network credential instead of a PSCredential object, this is very easy. You can get the corresponding <code>NetworkCredential</code> object from the <code>PSCredential</code>.
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt"><span class="kwrd">using</span> System.Net;</pre>
<pre> </pre>
<pre class="alt"><span class="kwrd">private</span> NetworkCredential getNetworkCredential()</pre>
<pre>{</pre>
<pre class="alt"> NetworkCredential cred = getPowershellCredential().GetNetworkCredential();</pre>
<pre> <span class="kwrd">return</span> cred;</pre>
<pre class="alt">}</pre>
</div>
<h2>Security aspects</h2>
Only the user account that encrypted the password can decrypt it (because Kerberos keys are used under the hood). This is illustrated in following screenshot. If you look carefully, you can check I did not cheat on the parameter.
<a href="https://wimbeck.be/wp-content/uploads/2015/03/secureString.png"><img src="https://wimbeck.be/wp-content/uploads/2015/03/secureString.png" alt="secureString" width="380" /></a>
<h2>Conclusion</h2>
The approach described here is simple, quick and secure. You need to run a few PowerShell commands and you can store passwords securely in your configuration files. Make sure to encrypt the required password using the service account that will be performing the decryption. Note that this technique is not limited to use in FIM deployments. You can use this technique in any .Net/Windows context.
<h2>References</h2>
<a href="http://blogs.technet.com/b/heyscriptingguy/archive/2013/03/26/decrypt-powershell-secure-string-password.aspx">Scripting Guy - Decrypt PowerShell Secure String Password</a>Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-76514367407943804212015-03-12T15:28:00.004+01:002015-03-30T18:25:12.990+02:00FIM2010: GUI for configuring your scheduler<h2>
Intro</h2>
I described in previous posts how I developed a windows service to schedule FIM. The configuration of this scheduler consists of XML files. Because it is not straightforward to ensure you have a consistent configuration that satisfies your needs, I developed an interface to help with the configuration.
The tool itself is built using the WPF framework (.NET 4.5) and has following requirements:
<br />
<ul>
<li>Path of the folder containing scheduler configuration files, including at least the following:
<ul>
<li>JobConfiguration.xml</li>
<li>job_scheduling_data_2_0.xsd</li>
<li>RunConfiguration.xml</li>
<li>RunSchedulingData.xsd</li>
</ul>
</li>
<li>Path of the folder containg a server export of the FIM Synchronization Engine</li>
</ul>
<a href="https://wimbeck.be/wp-content/uploads/2015/03/gui_browse.png"><img alt="gui_browse" src="https://wimbeck.be/wp-content/uploads/2015/03/gui_browse.png" width="380" /></a>Note that it is possible to use the tool on any server or workstation. After saving your changes you can transfer the configuration files to your FIM server.
<br />
<a name='more'></a><h2>
Configure triggers</h2>
The first tab, job configuration, allows you to add, delete, rename and configure triggers. Each trigger specifies a run configuration and on which schedule this run configuration will be fired. A typical delta schedule for FIM is "each 10 minutes during working hours". This can be translated to the cron expression "0 0/10 8-18 * * ?". The drop down list with run configurations is automatically propagated based on the existing run configurations.
Save config performs a validation of the cron expression using the job_scheduling_data_2_0.xsd schema file. If valid, JobConfiguration.xml is saved. A backup of the previous configuration is saved as well.
Reset config reloads the interface using the configuration in the file on disk.
<a href="https://wimbeck.be/wp-content/uploads/2015/03/gui_trigger1.png"><img alt="" src="https://wimbeck.be/wp-content/uploads/2015/03/gui_trigger1.png" width="380" /></a>
<br />
<h2>
Configure global parameters</h2>
The second tab, run configuration, offers three tabs to configure the RunConfiguration.xml file. The first of these tabs, global config, allows you to configure some global parameters:
<br />
<ul>
<li>Enable/disable clear run history</li>
<li>Specify how long to keep history in case clear run history is enabled</li>
<li>Delay in seconds for parallel sequences</li>
<li>Delay in seconds for linear sequences</li>
<li>Which run configuration to fire on trigger (event-driven scheduling)</li>
</ul>
Save config performs a validation using the RunSchedulingData.xsd schema file. If valid, RunConfiguration.xml is saved. This includes the settings of all three tabs under run configuration. A backup of the previous configuration is saved as well.
Reset config reloads the interface using the configuration in the file on disk.
<a href="https://wimbeck.be/wp-content/uploads/2015/03/gui_params.png"><img alt="gui_params" src="https://wimbeck.be/wp-content/uploads/2015/03/gui_params.png" width="380" /></a>
<br />
<h2>
Configure run configurations</h2>
The run configurations tab allows you to add, delete, rename and edit run configurations. Editing run configurations comes down to two things:
<br />
<ul>
<li>Configure a default run profile</li>
<li>Add, delete and reorder steps</li>
</ul>
<h3>
Default run profile</h3>
The default run profile is the top most action. Steps that do not have an action defined, take the action defined by their parent. This mechanism allows you to reuse sequences in combination with different profiles. You could have a run profile with default run profile "Full import full sync" and another with "Delta import delta sync". Then both of them could use the same sequence resulting in different actions.
This mechanism only works if using a naming convention for run profiles in all connectors in the FIM Synchronization Engine. Run profile names are case sensitive. If the scheduler tries to start a run profile that does not exist, the management agent will not be run. In the example here, the sequence Default will be run with run profile "Delta import delta sync".
<a href="https://wimbeck.be/wp-content/uploads/2015/03/gui_runconfig.png"><img alt="gui_runconfig" src="https://wimbeck.be/wp-content/uploads/2015/03/gui_runconfig.png" width="380" /></a>
<br />
<h3>
Steps</h3>
The add step button opens a new dialog where you can select the type of step. Because the server export info is read, a list of possible actions is available. However, as explained above, you do not need to specify an action.
<a href="https://wimbeck.be/wp-content/uploads/2015/03/gui_addStep.png"><img alt="gui_addStep" class="aligncenter size-full wp-image-206" height="268" src="https://wimbeck.be/wp-content/uploads/2015/03/gui_addStep.png" width="367" /></a>
<a href="https://wimbeck.be/wp-content/uploads/2015/03/gui_addStepMa.png"><img alt="gui_addStepMa" class="aligncenter size-full wp-image-207" height="300" src="https://wimbeck.be/wp-content/uploads/2015/03/gui_addStepMa.png" width="372" /></a>
<br />
<h2>
Configure sequences</h2>
The sequences tab allows you to add, delete, rename and edit sequences. The functionality provided here is identical to the one on the run configurations tab. Whether the sequence is executed as a linear or parallel sequence is defined by the step that calls the sequence, so a sequence can be defined as linear in one run configuration (or other sequence) and as parallel somewhere else.
<a href="https://wimbeck.be/wp-content/uploads/2015/03/gui_sequence.png"><img alt="gui_sequence" src="https://wimbeck.be/wp-content/uploads/2015/03/gui_sequence.png" width="380" /></a>
<br />
<h2>
Download</h2>
You can find the new release of the IS4U FIM scheduler on GitHub: <a href="https://github.com/wim-beck/FIM-Scheduler/releases/latest" target="_blank" title="Github - release 1.3">FIM-Scheduler Release</a>. The setup that installs the scheduler on the FIM server now also includes the GUI tool to configure it.Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-72689519614609482662015-02-24T16:10:00.002+01:002015-03-13T08:49:44.685+01:00FIM2010: Filter objects on export<h2>Intro</h2>
FIM allows you to filter objects on import through filters in the connector configuration. The same functionality is not available on export. There are two methods available to provision a selected set of objects to a target system through synchronization rules. This article shortly describes these two mechanisms and also describes a third using provisioning code.
<h2>Synchronization Rules</h2>
Synchronization rules allow codeless provisioning. It also allows you control over the population of objects you want to create in a certain target system.
<a name='more'></a>
<h3>Triplet</h3>
The first way of doing this is by defining a set of objects, a synchronization rule, a workflow that adds the synchronization rule to an object and a Management Policy Rule (MPR) that binds them together. In the set definition you can define filters. You can select a limited population of objects by configuring the correct filter on the set.
<a href="http://wimbeck.be/wp-content/uploads/2015/02/SyncRule.png"><img src="http://wimbeck.be/wp-content/uploads/2015/02/SyncRule.png" alt="triplet" width="350" /></a>
<h3>Scoping filter</h3>
The second method defines the filter directly on the synchronization rule, so you do not need a set, workflow and MPR. You simply define the conditions the target population needs to satisfy before they can be provisioned to the target system.
<a href="http://wimbeck.be/wp-content/uploads/2015/02/SyncRule2.png"><img src="http://wimbeck.be/wp-content/uploads/2015/02/SyncRule2.png" alt="outbound system scoping filter" width="350" /></a>
<a href="http://wimbeck.be/wp-content/uploads/2015/02/SyncRule3.png"><img src="http://wimbeck.be/wp-content/uploads/2015/02/SyncRule3.png" alt="Scope filter" width="350" /></a>
<h2>Coded provisioning</h2>
Coded provisioning allows for very complex provisioning and it is also the only option on projects where you use only the Synchronization Engine. What follows is only a portion of a more complex provisioning strategy:
<ul>
<li>Define an xml structure</li>
<li>For each connector that requires filtering on export, define the filters in xml</li>
<li>Make sure to use the same name for all connector configuration files and save them in their respective MaData folders</li>
<li>In the provisioning code, load all configuration files</li>
<li>For each object you consider for provisioning, check the filter</li>
</ul>
<h3>Sample configuration file</h3>
<!-- code formatted by http://manoli.net/csharpformat/ -->
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, "Courier New", Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<div class="csharpcode">
<pre class="alt"><span class="kwrd"><</span><span class="html">Configuration</span><span class="kwrd">></span></pre>
<pre> <span class="kwrd"><</span><span class="html">MaConfiguration</span> <span class="attr">Name</span><span class="kwrd">="AD MA"</span><span class="kwrd">></span></pre>
<pre class="alt"> <span class="kwrd"><</span><span class="html">Export-Filters</span><span class="kwrd">></span></pre>
<pre> <span class="kwrd"><</span><span class="html">Filter</span> <span class="attr">Name</span><span class="kwrd">="DepartmentFilter"</span> <span class="attr">IsActive</span><span class="kwrd">="true"</span><span class="kwrd">></span></pre>
<pre class="alt"> <span class="kwrd"><</span><span class="html">Condition</span> <span class="attr">Attribute</span><span class="kwrd">="Department"</span> <span class="attr">Operation</span><span class="kwrd">="Equals"</span> <span class="attr">IsActive</span><span class="kwrd">="true"</span><span class="kwrd">></span>Sales<span class="kwrd"></</span><span class="html">Condition</span><span class="kwrd">></span></pre>
<pre> <span class="kwrd"></</span><span class="html">Filter</span><span class="kwrd">></span></pre>
<pre class="alt"> <span class="kwrd"></</span><span class="html">Export-Filters</span><span class="kwrd">></span></pre>
<pre> <span class="kwrd"><</span><span class="html">MaConfiguration</span><span class="kwrd">></span></pre>
<pre class="alt"><span class="kwrd"></</span><span class="html">Configuration</span><span class="kwrd">></span></pre>
</div>
<h3>Sample source code</h3>
Following code is on itself not functional, but you get an idea of how the complete implementation can look like:
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt"><span class="kwrd">private</span> <span class="kwrd">bool</span> checkFilter(MVEntry mventry, Filter filter)</pre>
<pre>{</pre>
<pre class="alt"> <span class="kwrd">foreach</span> (FilterCondition condition <span class="kwrd">in</span> filter.Conditions)</pre>
<pre> {</pre>
<pre class="alt"> <span class="rem">// Return false if one of the conditions is not true.</span></pre>
<pre> <span class="kwrd">if</span> (!checkCondition(mventry, condition))</pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">return</span> <span class="kwrd">false</span>;</pre>
<pre class="alt"> }</pre>
<pre> }</pre>
<pre class="alt"> <span class="kwrd">return</span> <span class="kwrd">true</span>;</pre>
<pre>}</pre>
</div>
<p> </p>
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt"><span class="kwrd">private</span> <span class="kwrd">bool</span> checkCondition(MVEntry mventry, FilterCondition condition)</pre>
<pre>{</pre>
<pre class="alt"> <span class="kwrd">string</span> attributeValue = condition.Attribute;</pre>
<pre> <span class="kwrd">if</span> (mventry[attributeValue].IsPresent)</pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">if</span> (mventry[attributeValue].IsMultivalued)</pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">foreach</span> (Value value <span class="kwrd">in</span> mventry[attributeValue].Values)</pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">bool</span>? result = </pre>
<pre class="alt"> condition.Operation.Evaluate(value.ToString());</pre>
<pre> <span class="kwrd">if</span> (result.HasValue)</pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">return</span> result.Value;</pre>
<pre class="alt"> }</pre>
<pre> }</pre>
<pre class="alt"> <span class="kwrd">return</span> condition.Operation.DefaultValue;</pre>
<pre> }</pre>
<pre class="alt"> <span class="kwrd">else</span></pre>
<pre> {</pre>
<pre class="alt"> <span class="kwrd">bool</span>? result = condition.Operation.Evaluate(mventry[attributeValue].Value.ToString());</pre>
<pre> <span class="kwrd">if</span> (result.HasValue)</pre>
<pre class="alt"> {</pre>
<pre> <span class="kwrd">return</span> result.Value;</pre>
<pre class="alt"> }</pre>
<pre> <span class="kwrd">return</span> condition.Operation.DefaultValue;</pre>
<pre class="alt"> }</pre>
<pre> }</pre>
<pre class="alt"> <span class="kwrd">return</span> condition.Operation.DefaultValue;</pre>
<pre>}</pre>
</div>Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-50283341425051955072015-01-25T15:05:00.002+01:002015-03-13T08:50:02.810+01:00FIM 2010: SSPR with one-way trust<h2>Intro</h2>
This article describes and documents an SSPR setup between two AD forests with a one-way trust. FIM is deployed in the internal domain is4u.be. Users from the domain dmz.be are being imported and managed by FIM. There is a one-way incoming trust on the dmz.be domain. All prerequisites from the <a title="Password Reset Deployment Guide" href="https://technet.microsoft.com/en-us/library/ee534892%28v=ws.10%29.aspx" target="_blank">password reset deployment guide</a> are already satisfied.
<h2>DMZ connector configuration</h2>
SSPR requires that the DMZ connector service account has local logon rights on the FIM synchronization server. If the service account is from the DMZ domain, a two-way trust is required to allow this setting. Since this is not a valid option in this scenario, a service account from the IS4U domain needs to be delegated the proper rights on the DMZ domain. This includes at least the following:
<ol>
<li>Replicating directory access</li>
<li>Reset password</li>
</ol>
<a name='more'></a>
<h2>WMI verification</h2>
The configuration as is was tested and worked, but after a week, following the same scenario resulted in the following error:
<blockquote>An error has occurred. Please try again, and if the problem persists, contact your help desk or system administrator. (Error 3000)</blockquote>
Following up on the error, the event viewer gave following info:
<blockquote>Password Reset Activity could not find Mv record for user</blockquote>
This is a very clear error message indicating a problem with the WMI permissions. Checking up on this resulted in the conclusion that the permissions were set correctly. Lookups for accounts in the IS4U domain worked, but lookups for accounts in the DMZ domain failed.
<h2>Finding the PDC</h2>
Going back to the event viewer, we were given another clue:
<blockquote>DsGetDCName failed with 1355</blockquote>
A bit of researching learned us that the SetPassword call of SSPR always calls DsGetDCName because SSPR needs to find and target the PDC (domain controller with the PDC emulator role). This call seems to fail. We tried getting more info by running this specific call via nltest <code>nltest /dsgetdc:dmz /netbios</code>, but failed with following message:
<blockquote>Getting DC name failed: Status = 1355 0x54b ERROR_NO_SUCH_DOMAIN</blockquote>
However, resolving the FQDN using <code>nltest /dsgetdc:dmz.be /netbios</code> succeeded. And, even more strange, retrying to resolve using the netbios name did work! Some googling pointed to caching of certain information, which explained why the netbios lookup works after the FQDN lookup and why the initial configuration worked and then broke a week later.
<h2>WINS</h2>
NetBIOS recognizes domain controllers by the [1C] service record registration, but we could not find the correct WINS configuration, maybe because of the one-way trust.
<h2>Solution</h2>
The solution involved changing the advanced IP configuration settings. By adding the <code>is4u.be</code> and <code>be</code> suffixes the DsGetDCName call is enforced to always resolve the FQDN by searching for dmz.be instead of dmz.
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-XdwNg1W9ogQ/VMT3TSVFGDI/AAAAAAAAAIU/kdvt-SkADqQ/s1600/ipSettings.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-XdwNg1W9ogQ/VMT3TSVFGDI/AAAAAAAAAIU/kdvt-SkADqQ/s320/ipSettings.png" /></a></div>
<h2>References</h2>
<ul>
<li><a href="https://social.technet.microsoft.com/Forums/en-US/17163eda-b3b0-4a8e-b1ca-ded682764180/extranet-self-service-reset-portal-for-otp-mail-only-via-fim-to-an-untrusted-ad?forum=ilm2">Forum thread on SSPR</a></li>
<li><a title="Password Reset Deployment Guide" href="https://technet.microsoft.com/en-us/library/ee534892%28v=ws.10%29.aspx" target="_blank">Password Reset Deployment Guide</a></li>
<li><a title="Verify WMI permissions" href="http://setspn.blogspot.be/2010/09/fim-sspr-verify-wmi-permissions.html" target="_blank">Verify WMI permissions</a></li>
<li><a title="Details about WMI calls" href="http://blog.msresource.net/2011/05/03/fim-self-service-password-reset-sspr-and-active-directory-password-policy/" target="_blank">Details about WMI calls</a></li>
<li><a href="https://technet.microsoft.com/nl-be/library/cc759550%28v=ws.10%29.aspx" title="DNS Support for Active Directory">DNS Support for Active Directory</a></li>
</ul>Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-82440335018990223742014-12-15T14:56:00.000+01:002015-03-13T08:50:29.792+01:00FIM 2010: Eliminating equal precedence<style type="text/css">
.tg {border-collapse:collapse;border-spacing:0;border-color:#ccc;}
.tg td{font-family:Arial, sans-serif;font-size:14px;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:#ccc;color:#333;background-color:#fff;}
.tg th{font-family:Arial, sans-serif;font-size:14px;font-weight:normal;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:#ccc;color:#333;background-color:#f0f0f0;}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, "Courier New", Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<h3>Intro</h3>
<p>
Precedence can be tricky in certain scenarios. Imagine you want to make FIM master for a given attribute, but you need an initial flow from another data source. A good example is the LDAP distinguished name. If you have a rule that builds the DN automatically based on a base DN and one or more attribute values, the object is provisioned with the correct DN on export. But when you want to visualize this DN in the FIM portal, you need to be able to flow it back. If FIM is master over the distinguished name attribute, this flow will be skipped "Not precedent".
</p>
<p>
So you have to consider the option of using equal precedence, since manual precedence is not possible in combination with the FIM MA. But equal precedence is dependent on the synchronization cycle order: "the last one to write the attribute wins". Therefore it is not an option if FIM needs to be the absolute master of the DN attribute and you want to make sure that it always has the value you expect it to have.
</p>
<a name='more'></a>
<h3>Single valued attribute</h3>
<p>
The solution I came up with to work around this issue involves using two separate metaverse attributes. The flow is illustrated by following table.
</p>
<table class='tg'>
<tr>
<th>Datasource</th>
<th></th>
<th>Metaverse</th>
<th></th>
<th>FIM Portal</th>
</tr>
<tr>
<td rowspan="2">ds_attr</td>
<td>→</td>
<td>mv_attr1</td>
<td>→</td>
<td rowspan="2">fim_attr</td>
</tr>
<tr>
<td>←</td>
<td>mv_attr2</td>
<td>←</td>
</tr>
</table>
<p>
By using two metaverse attributes, both requirements are satisfied:
<ul>
<li>FIM is master over the value flowed to the data source</li>
<li>The actual value from the data source is flowed back to FIM</li>
</ul>
</p>
<h3>Multi valued attribute</h3>
<p>
We tried to apply this solution for multi valued attributes as well. A well known attribute that fits this use case is proxyAddresses. Initially, some exchange attributes are set in AD, such as mailNickName and homeMdb. Exchange generates some proxyAddresses based on defined rules. These aliases need to be available in FIM if FIM is used to manage this information.
</p>
<p>
To our surprise, the solution did not work in this case. After some investigation, the explanation was simple. The two metaverse attributes were not equal, which resulted in unexpected values after two or more synchronization cycles.
</p>
<ol>
<li>Delta import delta sync FIM MA: fim_attr flows to mv_attr2</li>
<li>Export AD MA: mv_attr2 flows to ds_attr</li>
<li>Delta import delta sync AD MA: ds_attr flows to mv_attr1</li>
</ol>
<p>
The third step is expected, but does not update the entire value of mv_attr1 (key here is "entire"). The delta import delta sync step checks only changed attributes (and for multi valued attributes only changed entries). The value of ds_attr was just exported, so FIM compares its value with the originating metaverse attribute, which is mv_attr2. Since the values of mv_attr2 and ds_attr match, the export is successfully confirmed. But the value of mv_attr1 remains unchanged and is different from mv_attr2. In the next synchronization cycle, the value of mv_attr1 will be synchronized to the value of fim_attr, which results in an unwanted value.
</p>
<p>
If full synchronizations are used, everything works as expected because all entries in the multi valued attribute are taken into consideration. On a delta sync, only the changed fields are evaluated. We applied an advanced import flow to allow the flow of addresses generated by Exchange for newly create mailboxes.
</p>
<!-- code formatted by http://manoli.net/csharpformat/
if (csentry["ProxyAddresses"].IsPresent && mventry["ProxyAddresses"].Values.Count == 0)
{
mventry["ProxyAddresses"].Values = csentry["ProxyAddresses"].Values;
}
-->
<div class="csharpcode">
<pre class="alt"><span class="kwrd">if</span> (csentry[<span class="str">"ProxyAddresses"</span>].IsPresent </pre>
<pre> && mventry[<span class="str">"ProxyAddresses"</span>].Values.Count == 0)</pre>
<pre class="alt">{</pre>
<pre> mventry[<span class="str">"ProxyAddresses"</span>].Values = </pre>
<pre class="alt"> csentry[<span class="str">"ProxyAddresses"</span>].Values;</pre>
<pre>}</pre>
</div>
<h3>Summary</h3>
<p>
The proposed configuration allows two-way updates while enforcing precedence for one data source. However, it does not work for multi valued attributes using delta synchronizations.
</p>Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-40036734266427254842014-12-03T14:58:00.001+01:002015-03-13T15:39:49.695+01:00FIM on Azure<div class="MsoNormal">
While deploying your Forefront Identity Manager labs in your own local virtual
environment is convenient, it does consume a lot of your precious disk drive
space and there is no questioning the impact of hardware failure. So why not
move your virtualization layer to the cloud and let Azure take care of the
storage, networking and compute infrastructure for you? This post will go over
the steps we took in order to successfully automate the deployment of our FIM
lab environments on the Microsoft Azure platform.<br />
<br /></div>
<h3>
<a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><span lang="EN-US" style="color: #cccccc;"><b>Azure infrastructure fundamentals</b></span></h3>
<div class="MsoNormal">
<span lang="EN-US">In order to create domain joined environments in Azure, there are four components we need:</span>
<br />
<br />
<div class="MsoNormal">
<span lang="EN-US">1. An affinity group<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Having our resources deployed in the same region (data center) is a fair option, but there is no certainty these resources are also located in the same cluster within that data center. Using affinity groups, we can define a container in which all our virtual machines are physically placed close together. This improves latency, performance and thereby cost.<o:p></o:p></span>
</div>
<div class="MsoNormal">
<span lang="EN-US">2. A cloud service<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">This component is responsible for hosting our virtual machines. It gets assigned a public IP address, making it possible for you to connect to your environment from any location using your own defined endpoints.<o:p></o:p></span>
</div>
<div class="MsoNormal">
<span lang="EN-US">3. A virtual network<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">In a domain joined setup it is necessary your machines can talk to each other. Using a VPN, we make sure VM's are deployed in the same IP range. These VM's can be assigned a static internal IP address which makes it possible to define your domain controller as the DNS server for the virtual network.<o:p></o:p></span>
</div>
<div class="MsoNormal">
<span lang="EN-US">4. A storage container<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Each deployment gets its own container to host their virtual hard disks (VHD's) under a storage account which is linked to the subscription of the deployment's cloud service.<o:p></o:p></span><br />
<span lang="EN-US"><br /></span>
<span lang="EN-US"></span><br />
<a name='more'></a></div>
</div>
<h3>
<span lang="EN-US" style="color: #cccccc;">Setup
process</span></h3>
<div class="MsoNormal">
<span lang="EN-US">The whole
process is executed using the Azure library module for PowerShell. First we
authenticate our Azure account and select the current subscription we wish to
use for our setup. We then assign a valid storage account to this subscription.
<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">Combining
the infrastructure elements we can set up our Azure environment in which our
lab will be deployed. We first check and create a valid cloud service name in
an affinity group. Then we retrieve the xml configuration. In this
configuration we insert our new VPN, and DNS server to use. The address space
for this virtual network will be 10.0.0.0/16, with a DNS server referenced to
the IP address of the domain controller. Finally we create a new storage
container for this lab. And that's it, our environment is ready for deployment.<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">Next up we
will provision our servers. We have preconfigured each server up to the point
of domain joining and captured a generalized VHD of this state. These VHD's are
stored as images on our storage account so we can use them over and over again
as a base machine for each server. Each machine has it's own parametrised
configuration consisting of the VM name, size, location of the VHD, image to
use and endpoints to assign for both RDP and remote PowerShell. We assign it
the correct static IP address and subnet name defined in the VPN configuration.
The next step depends on the type of server, there are 2 ways of defining the
provisioning configuration. <o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">We start
with the AD server, which will not include any domain parameters. We define our
provisioning parameters just like we would provision a standalone machine. When
the machine is booted up, we run a remote script through the PowerShell
endpoint and promote it to a domain controller. And voila, we created a domain
within our VPN. <o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">Next up we
provision the other type (non-AD) of servers to the domain. Sadly, it is
currently not supported to send parallel requests on the same cloud service
using the Azure API. Instead, and because the domain provisioning configuration
is the same for all the other servers, we can create the instance for each
server and send them all together in one creation bulk request. This is as
close as it gets to parallel provisioning of multiple servers in the same cloud
service.<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">After the
domain provisioning step has been executed, we automate the configuration of
each server using PowerShell scripts which run on the PowerShell endpoint
defined for that server.</span><br />
<span lang="EN-US"><br /></span></div>
<h3>
<span style="color: #cccccc;">
Faster deployments?</span></h3>
<div class="MsoNormal">
<a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><span lang="EN-US">Depending
on the chosen server setup, this process can take quite a while to complete
(mainly the installation of software during configuration).A basic setup
consists of an AD / SQL / FIM server and takes about half an hour to complete
in it's most basic configuration. Available optional servers include Exchange,
BHOLD, and SCSM (both management server and data warehouse) which takes the
total server count up to 7 servers. A full setup takes a lot longer and for
this reason, we implemented a complementary way of setting up our labs. We
started from a fully working lab setup and captured the VM's as specialized
images as opposed to generalized in the previous method. This way you can use
these images in a new azure environment in a slightly different provisioning
configuration and have your basic deployment set up in less than 6 minutes! The
drawback of this, is that the lab is not completely configurable as it uses a
saved state (snapshot) of a previous domain joined setup.</span><br />
<span lang="EN-US"><br /></span></div>
<h3>
<span style="color: #cccccc;">
The lazy way</span></h3>
<div class="MsoNormal">
<a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><a href="https://www.blogger.com/blogger.g?blogID=698829246057037173" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"></a><span lang="EN-US">Having a
PowerShell cmdlets library at our disposal is really nice, although running
these commands ourselves is not really what we were after. So we made our own
WPF application to create an interface which will invoke the underlying
PowerShell scripts (both the Azure module locally and configuration of servers
remotely) in C# using a PowerShell class. During setup, passwords for all the (service)
accounts are generated and stored in an xml file which is compliant for import
to KeePass. The RDP configuration is automatically generated in an RDG file which
can be opened with RDCManager (<a href="http://www.microsoft.com/en-us/download/details.aspx?id=44989">http://www.microsoft.com/en-us/download/details.aspx?id=44989</a>).
<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">Let’s round up with some visual material!<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<div style="text-align: center;">
<span lang="EN-US"><span style="font-family: inherit;">Account
setup:</span><o:p></o:p></span></div>
</div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shapetype
id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t"
path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f">
<v:stroke joinstyle="miter"/>
<v:formulas>
<v:f eqn="if lineDrawn pixelLineWidth 0"/>
<v:f eqn="sum @0 1 0"/>
<v:f eqn="sum 0 0 @1"/>
<v:f eqn="prod @2 1 2"/>
<v:f eqn="prod @3 21600 pixelWidth"/>
<v:f eqn="prod @3 21600 pixelHeight"/>
<v:f eqn="sum @0 0 1"/>
<v:f eqn="prod @6 1 2"/>
<v:f eqn="prod @7 21600 pixelWidth"/>
<v:f eqn="sum @8 21600 0"/>
<v:f eqn="prod @7 21600 pixelHeight"/>
<v:f eqn="sum @10 21600 0"/>
</v:formulas>
<v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/>
<o:lock v:ext="edit" aspectratio="t"/>
</v:shapetype><v:shape id="Picture_x0020_4" o:spid="_x0000_i1031" type="#_x0000_t75"
style='width:453.75pt;height:363pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:\Users\Glenn\AppData\Local\Temp\msohtmlclip1\01\clip_image001.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><span lang="EN-US"><o:p></o:p></span></div>
<div class="MsoNormal">
</div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:line id="Straight_x0020_Connector_x0020_3"
o:spid="_x0000_s1026" style='position:absolute;z-index:251659264;visibility:visible;
mso-wrap-style:square;mso-height-percent:0;mso-wrap-distance-left:9pt;
mso-wrap-distance-top:0;mso-wrap-distance-right:9pt;
mso-wrap-distance-bottom:0;mso-position-horizontal:absolute;
mso-position-horizontal-relative:text;mso-position-vertical:absolute;
mso-position-vertical-relative:text;mso-height-percent:0;
mso-height-relative:margin' from="206.65pt,88.15pt" to="306.4pt,88.15pt"
o:gfxdata="UEsDBBQABgAIAAAAIQC75UiUBQEAAB4CAAATAAAAW0NvbnRlbnRfVHlwZXNdLnhtbKSRvU7DMBSF
dyTewfKKEqcMCKEmHfgZgaE8wMW+SSwc27JvS/v23KTJgkoXFsu+P+c7Ol5vDoMTe0zZBl/LVVlJ
gV4HY31Xy4/tS3EvRSbwBlzwWMsjZrlprq/W22PELHjb51r2RPFBqax7HCCXIaLnThvSAMTP1KkI
+gs6VLdVdad08ISeCho1ZLN+whZ2jsTzgcsnJwldluLxNDiyagkxOquB2Knae/OLUsyEkjenmdzb
mG/YhlRnCWPnb8C898bRJGtQvEOiVxjYhtLOxs8AySiT4JuDystlVV4WPeM6tK3VaILeDZxIOSsu
ti/jidNGNZ3/J08yC1dNv9v8AAAA//8DAFBLAwQUAAYACAAAACEArTA/8cEAAAAyAQAACwAAAF9y
ZWxzLy5yZWxzhI/NCsIwEITvgu8Q9m7TehCRpr2I4FX0AdZk2wbbJGTj39ubi6AgeJtl2G9m6vYx
jeJGka13CqqiBEFOe2Ndr+B03C3WIDihMzh6RwqexNA281l9oBFTfuLBBhaZ4ljBkFLYSMl6oAm5
8IFcdjofJ0z5jL0MqC/Yk1yW5UrGTwY0X0yxNwri3lQgjs+Qk/+zfddZTVuvrxO59CNCmoj3vCwj
MfaUFOjRhrPHaN4Wv0VV5OYgm1p+LW1eAAAA//8DAFBLAwQUAAYACAAAACEARDkxTrgBAAApBAAA
HwAAAGNsaXBib2FyZC9kcmF3aW5ncy9kcmF3aW5nMS54bWykU8tu3CAU3VfqPyD2jR/TGSVWPFlM
22yiNuq0H3CFwUbFFwuI4/n7gI09I2ukqO0G8TiPey5w/zC0ivTcWKmxpNlNSglHpiuJdUl///r2
6ZYS6wArUBp5SU/c0of9xw/3UNQGukYy4hXQFlDSxrmuSBLLGt6CvdEdR38mtGnB+aWpk8rAq1du
VZKn6S5pQSLdn6W+gAPyYuQ/SCnN/vDqANiD9ZKKFZc7sUbF/l8ZCuwfTXfsnk2onH3vnw2RVUl9
5xBa3yKaxIMI88tkxarPAoMwbcBrIcgwqpzCOGrwwRE2bbLzLmt+XMGy5usVtDeeDPzkwpQNeOyC
K/aHMF1n2cxZjs6ArBtHDhqRM6cN2Sz5InXOd6Fko+Iq3Gabf863vlE+TJ5md5t0FTTLd7vbfEvJ
EncJAEVnrHvkuiVhUlIlkY+PB/on66YiZkiIppC8lvRuGwyn07kq606KT5CfXPjL8y3ORqnx6fKD
MqQHVVJgjKPLIl2hRweakEotxPR9YsQHKhfCd/FvyAtjdNZ4JrcStbnm7oa5ZDHhxwcYU/uGxttP
Vp9kRMVPHX7i5Xr/BgAA//8DAFBLAwQUAAYACAAAACEAtjsEIlQGAAALGgAAGgAAAGNsaXBib2Fy
ZC90aGVtZS90aGVtZTEueG1s7FlLbxs3EL4X6H9Y7L2x3oqNyIGtR9zGToJISZEjpaV2GXOXC5Ky
o1uRHAsUKJoWPTRAbz0UbQMkQC/pr3Gbok2B/IUOuQ+RElU7RgoYQSzA2J39Zjicmf2G5F65+iCm
3hHmgrCk41cvVXwPJxMWkCTs+HdGg48u+56QKAkQZQnu+HMs/KvbH35wBW1NKEnHDPFgFOEYe2Ao
EVuo40dSplsbG2ICYiQusRQn8GzKeIwk3PJwI+DoGAaI6UatUmltxIgk/jZYlMpQn8K/RAolmFA+
VGawl6AYRr85nZIJ1tjgsKoQYi66lHtHiHZ8sBmw4xF+IH2PIiHhQcev6D9/Y/vKBtrKlahco2vo
DfRfrpcrBIc1PSYPx+WgjUaz0dop7WsAlau4frvf6rdKexqAJhOYaeaLabO5u7nba+ZYA5RdOmz3
2r161cIb9usrPu801c/Ca1Bmv7GCHwy6EEULr0EZvrmCbzTatW7DwmtQhm+t4NuVnV6jbeE1KKIk
OVxBV5qtereYbQmZMrrnhG82G4N2LTe+QEE1lNWlhpiyRK6rtRjdZ3wAAAWkSJLEk/MUT9EEarKL
KBlz4u2TMILCS1HCBIgrtcqgUof/6tfQVzoiaAsjQ1v5BZ6IFZHyxxMTTlLZ8T8Bq74Bef3ip9cv
nnknD5+fPPz15NGjk4e/ZIYsrT2UhKbWqx++/OfJZ97fz75/9fhrN16Y+D9+/vz3375yA2GmixC8
/Obpn8+fvvz2i79+fOyA73A0NuEjEmPh3cDH3m0Ww8R0CGzP8Zi/mcYoQsTU2ElCgRKkRnHY78vI
Qt+YI4ocuF1sR/AuB4pxAa/N7lsODyM+k8Rh8XoUW8ADxugu484oXFdjGWEezZLQPTifmbjbCB25
xu6ixMpvf5YCtxKXyW6ELTdvUZRIFOIES089Y4cYO2Z3jxArrgdkwplgU+ndI94uIs6QjMjYqqaF
0h6JIS9zl4OQbys2B3e9XUZds+7hIxsJbwWiDudHmFphvIZmEsUukyMUUzPg+0hGLieHcz4xcX0h
IdMhpszrB1gIl85NDvM1kn4d6MWd9gM6j20kl+TQZXMfMWYie+ywG6E4dWGHJIlM7MfiEEoUebeY
dMEPmP2GqHvIA0rWpvsuwVa6T2eDO8CspkuLAlFPZtyRy2uYWfU7nNMpwppqgPgtPo9Jciq5L9F6
8/+ldSDSl989cczqohL6DifON2pvicbX4ZbJu8t4QC4+d/fQLLmF4XVZbWDvqfs9dfvvPHWve5/f
PmEvOBroWy0Vs6W6XrjHa9ftU0LpUM4p3hd66S6gMwUDECo9vT/F5T4ujeBSvckwgIULOdI6Hmfy
UyKjYYRSWN9XfWUkFLnpUHgpE7Ds12KnbYWns/iABdl2tVpVW9OMPASSC3mlWcphqyEzdKu92IKV
5rW3od4qFw4o3TdxwhjMdqLucKJdCFWQ9MYcguZwQs/srXix6fDisjJfpGrFC3CtzAosnTxYcHX8
ZgNUQAl2VIjiQOUpS3WRXZ3Mt5npdcG0KgDWEUUFLDK9qXxdOz01u6zUzpBpywmj3GwndGR0DxMR
CnBenUp6FjfeNNebi5Ra7qlQ6PGgtBZutC//lxfnzTXoLXMDTUymoIl33PFb9SaUzASlHX8K2364
jFOoHaGWvIiGcGA2kTx74c/DLCkXsodElAVck07GBjGRmHuUxB1fTb9MA000h2jfqjUghAvr3CbQ
ykVzDpJuJxlPp3gizbQbEhXp7BYYPuMK51Otfn6w0mQzSPcwCo69MZ3x2whKrNmuqgAGRMDpTzWL
ZkDgOLMkskX9LTWmnHbN80RdQ5kc0TRCeUcxyTyDayov3dF3ZQyMu3zOEFAjJHkjHIeqwZpBtbpp
2TUyH9Z23dOVVOQM0lz0TItVVNd0s5g1QtEGlmJ5viZveFWEGDjN7PAZdS9T7mbBdUvrhLJLQMDL
+Dm67hkaguHaYjDLNeXxKg0rzs6ldu8oJniKa2dpEgbrtwqzS3Ere4RzOBCeq/OD3nLVgmharCt1
pF2fJg5Q6o3DaseHzwNwPvEAruADgw+ympLVlAyu4KsBtIvsqL/j5xeFBJ5nkhJTLyT1AtMoJI1C
0iwkzULSKiQt39Nn4vAdRh2H+15x5A09LD8iz9cW9veb7X8BAAD//wMAUEsDBBQABgAIAAAAIQCc
ZkZBuwAAACQBAAAqAAAAY2xpcGJvYXJkL2RyYXdpbmdzL19yZWxzL2RyYXdpbmcxLnhtbC5yZWxz
hI/NCsIwEITvgu8Q9m7SehCRJr2I0KvUBwjJNi02PyRR7Nsb6EVB8LIws+w3s037sjN5YkyTdxxq
WgFBp7yenOFw6y+7I5CUpdNy9g45LJigFdtNc8VZ5nKUxikkUigucRhzDifGkhrRykR9QFc2g49W
5iKjYUGquzTI9lV1YPGTAeKLSTrNIXa6BtIvoST/Z/thmBSevXpYdPlHBMulFxagjAYzB0pXZ501
LV2BiYZ9/SbeAAAA//8DAFBLAQItABQABgAIAAAAIQC75UiUBQEAAB4CAAATAAAAAAAAAAAAAAAA
AAAAAABbQ29udGVudF9UeXBlc10ueG1sUEsBAi0AFAAGAAgAAAAhAK0wP/HBAAAAMgEAAAsAAAAA
AAAAAAAAAAAANgEAAF9yZWxzLy5yZWxzUEsBAi0AFAAGAAgAAAAhAEQ5MU64AQAAKQQAAB8AAAAA
AAAAAAAAAAAAIAIAAGNsaXBib2FyZC9kcmF3aW5ncy9kcmF3aW5nMS54bWxQSwECLQAUAAYACAAA
ACEAtjsEIlQGAAALGgAAGgAAAAAAAAAAAAAAAAAVBAAAY2xpcGJvYXJkL3RoZW1lL3RoZW1lMS54
bWxQSwECLQAUAAYACAAAACEAnGZGQbsAAAAkAQAAKgAAAAAAAAAAAAAAAAChCgAAY2xpcGJvYXJk
L2RyYXdpbmdzL19yZWxzL2RyYXdpbmcxLnhtbC5yZWxzUEsFBgAAAAAFAAUAZwEAAKQLAAAAAA==
" strokecolor="#5b9bd5 [3204]" strokeweight="7.5pt">
<v:stroke joinstyle="miter"/>
</v:line><![endif]--><!--[if !vml]--><span style="height: 11px; margin-left: 270px; margin-top: 112px; mso-ignore: vglayout; position: absolute; width: 144px; z-index: 251659264;"></span><!--[endif]--><!--[if gte vml 1]><v:shape
id="Picture_x0020_2" o:spid="_x0000_i1030" type="#_x0000_t75" style='width:453pt;
height:362.25pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:\Users\Glenn\AppData\Local\Temp\msohtmlclip1\01\clip_image004.png"
o:title="2014-12-03_10h06_15"/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><span lang="EN-US"><o:p></o:p></span></div>
<div class="MsoNormal">
<div style="text-align: center;">
<br /></div>
</div>
<div class="MsoNormal">
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-x_5QoRr6Fks/VH8QmcfrVGI/AAAAAAAAABU/VHGymJ9MXnI/s1600/2014-12-03_13h39_14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="http://3.bp.blogspot.com/-x_5QoRr6Fks/VH8QmcfrVGI/AAAAAAAAABU/VHGymJ9MXnI/s1600/2014-12-03_13h39_14.png" height="318" width="400" /></a></div>
<br />
<div style="text-align: center;">
<br /></div>
</div>
<div class="MsoNormal">
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-zqy_cwT4PD4/VH8RIHnX0_I/AAAAAAAAABg/C_vHGqO64zw/s1600/2014-12-03_10h06_15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="http://1.bp.blogspot.com/-zqy_cwT4PD4/VH8RIHnX0_I/AAAAAAAAABg/C_vHGqO64zw/s1600/2014-12-03_10h06_15.png" height="318" width="400" /></a></div>
</div>
<div class="MsoNormal">
<div style="text-align: center;">
<br /></div>
</div>
<div class="MsoNormal">
<div style="text-align: center;">
<span style="font-family: inherit;">Environment
setup:</span><o:p></o:p></div>
<div style="text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-u3PB00shw74/VH8QmtqiXfI/AAAAAAAAAA4/99KQaCmsGPA/s1600/2014-12-03_13h39_18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="http://4.bp.blogspot.com/-u3PB00shw74/VH8QmtqiXfI/AAAAAAAAAA4/99KQaCmsGPA/s1600/2014-12-03_13h39_18.png" height="318" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-skIRZg1XkKo/VH8Qll35peI/AAAAAAAAAAs/t2no4-ZJAac/s1600/2014-12-03_12h36_33.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="http://1.bp.blogspot.com/-skIRZg1XkKo/VH8Qll35peI/AAAAAAAAAAs/t2no4-ZJAac/s1600/2014-12-03_12h36_33.png" height="318" width="400" /></a></div>
<div style="text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-gynTqU8eOGw/VH8QlqtPIoI/AAAAAAAAAAw/ObX0_ATLpH0/s1600/2014-12-03_10h06_22.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="http://2.bp.blogspot.com/-gynTqU8eOGw/VH8QlqtPIoI/AAAAAAAAAAw/ObX0_ATLpH0/s1600/2014-12-03_10h06_22.png" height="318" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
<div class="MsoNormal">
<div style="text-align: center;">
<div class="MsoNormal">
<span style="font-family: inherit;"><span lang="EN-US">Redeploy a default lab with a limited set of parameters</span><span style="font-size: 13.5pt; line-height: 107%;">:</span></span><br />
<span style="font-family: Calibri, sans-serif; font-size: 13.5pt; line-height: 107%;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-XPKZ7zarEeo/VH8jLu1FWDI/AAAAAAAAACE/sDLt0EOKAQs/s1600/2014-12-03_14h01_11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-XPKZ7zarEeo/VH8jLu1FWDI/AAAAAAAAACE/sDLt0EOKAQs/s1600/2014-12-03_14h01_11.png" height="318" width="400" /></a></div>
<span style="font-family: Calibri, sans-serif; font-size: 13.5pt; line-height: 107%;"><br /></span>
<span style="font-family: Calibri, sans-serif; font-size: 13.5pt; line-height: 107%;"><br /></span>
<span style="font-family: inherit; line-height: 107%;">Fully customize your deployment parameters for each server:</span><br />
<span style="font-family: Calibri, sans-serif; font-size: 13.5pt; line-height: 107%;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-Aax0gzzKOQ0/VH8jEWlUuQI/AAAAAAAAAB8/euPILuXdkLQ/s1600/2014-12-03_14h02_52.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-Aax0gzzKOQ0/VH8jEWlUuQI/AAAAAAAAAB8/euPILuXdkLQ/s1600/2014-12-03_14h02_52.png" height="318" width="400" /></a></div>
<span style="font-family: Calibri, sans-serif; font-size: 13.5pt; line-height: 107%;"><br /></span>
<span style="font-family: Calibri, sans-serif; font-size: 13.5pt; line-height: 107%;"><br /></span>
<span lang="EN-US" style="font-family: inherit;">Start a full installation or manually proceed with each step:</span><br />
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<a href="http://4.bp.blogspot.com/-ZMpCmYybel0/VH8T7VH2BzI/AAAAAAAAABs/Z9OlI-uzTw4/s1600/2014-12-03_14h43_46.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-ZMpCmYybel0/VH8T7VH2BzI/AAAAAAAAABs/Z9OlI-uzTw4/s1600/2014-12-03_14h43_46.png" height="318" width="400" /></a></div>
</div>
</div>
Anonymoushttp://www.blogger.com/profile/04486974454248796415noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-37143680675288586532014-11-19T20:03:00.003+01:002015-03-13T08:59:53.167+01:00FIM 2010: Event driven scheduling<!-- code formatted by http://manoli.net/csharpformat/ -->
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, "Courier New", Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<h2>Intro</h2>
In a previous post I described how I implemented a windows service for scheduling Forefront Identity Manager.<br />
<br />
Since then, me and my colleagues used it in every FIM project. For one project I was asked if it was possible to trigger the synchronization "on demand". A specific trigger for a synchronization cycle for example, was the creation of a user in the FIM portal.
After some brainstorming and Googling, we came up with a solution.<br />
<h2>Trigger</h2>
We asked ourselves following question: "Is it possible to send a signal to our existing Windows service to start a synchronization cycle?". All the functionality for scheduling was already there, so it seemed reasonable to investigate and explore this option.
As it turns out, it is possible to send a signal to a Windows service and the implementation turned out to be very simple (and simple is good, right?).
<a name='more'></a>
<br />
<br />
In addition to the scheduling on predefined moments defined in the job configuration file, which is implemented through the Quartz framework, we started an extra thread:<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt">while (<span class="kwrd">true</span>)</pre>
<pre>{</pre>
<pre class="alt"> <span class="kwrd">if</span> (scheduler.GetCurrentlyExecutingJobs().Count == 0 </pre>
<pre> && !paused)</pre>
<pre class="alt"> {</pre>
<pre> scheduler.PauseAll();</pre>
<pre class="alt"> <span class="kwrd">if</span> (DateTime.Compare(StartSignal, LastEndTime) > 0)</pre>
<pre> {</pre>
<pre class="alt"> running = <span class="kwrd">true</span>;</pre>
<pre> StartSignal = DateTime.Now;</pre>
<pre class="alt"> LastEndTime = StartSignal;</pre>
<pre> SchedulerConfig schedulerConfig = </pre>
<pre class="alt"> <span class="kwrd">new</span> SchedulerConfig(runConfigurationFile);</pre>
<pre> <span class="kwrd">if</span> (schedulerConfig != <span class="kwrd">null</span>)</pre>
<pre class="alt"> {</pre>
<pre> schedulerConfig.RunOnDemand();</pre>
<pre class="alt"> }</pre>
<pre> <span class="kwrd">else</span></pre>
<pre class="alt"> {</pre>
<pre> logger.Error(<span class="str">"Scheduler configuration not found."</span>);</pre>
<pre class="alt"> <span class="kwrd">throw</span> <span class="kwrd">new</span> JobExecutionException</pre>
<pre> (<span class="str">"Scheduler configuration not found."</span>);</pre>
<pre class="alt"> }</pre>
<pre> running = <span class="kwrd">false</span>;</pre>
<pre class="alt"> }</pre>
<pre> scheduler.ResumeAll();</pre>
<pre class="alt"> }</pre>
<pre> <span class="rem">// 5 second delay</span></pre>
<pre class="alt"> Thread.Sleep(5000);</pre>
<pre>}</pre>
</div>
<h2>StartSignal</h2>
First thing it does is check if one of the time-triggered schedules is not running and the service is not paused. Then it checks to see if an on-demand trigger was received by checking the StartSignal timestamp. So as you can see, the StartSignal timestamp
is the one controlling the action. If the service receives a signal to start a synchronization schedule, it simply sets the StartSignal parameter:<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt"><span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> OnCustomCommand(<span class="kwrd">int</span> command)</pre>
<pre>{</pre>
<pre class="alt"> <span class="kwrd">if</span> (command == ONDEMAND)</pre>
<pre> {</pre>
<pre class="alt"> StartSignal = DateTime.Now;</pre>
<pre> }</pre>
<pre class="alt">}</pre>
</div>
<br />
If you want to know more about developing custom activities, this <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ee652258%28v=vs.100%29.aspx">
article</a> is a good starting point.<br />
<br />
The first thing it does next if a signal was received, is pause the time-triggered mechanism. If the synchronization cycle finishes the time-triggered scheduling is resumed. The beautiful thing about this way of working is that the two separate mechanisms work alongside each other. The time-triggered schedule is not fired if an on-demand schedule is running and vice versa. If a signal was sent during a period of time the service was paused, the on-demand schedule will fire as soon as the service is resumed. The StartSignal timestamp will take care of that.
<h2>StartSync</h2>
So, how do you send a signal to this service, you ask? This is also fairly straightforward. I implemented the FIM portal scenario I described above by implementing a custom C# workflow with a single code activity:
<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt"><span class="kwrd">using</span> System.ServiceProcess;</pre>
<pre> </pre>
<pre class="alt"><span class="kwrd">private</span> <span class="kwrd">const</span> <span class="kwrd">int</span> OnDemand = 234;</pre>
<pre> </pre>
<pre class="alt"><span class="kwrd">private</span> <span class="kwrd">void</span> startSync(){</pre>
<pre> ServiceController is4uScheduler = </pre>
<pre class="alt"> <span class="kwrd">new</span> ServiceController(<span class="str">"IS4UFIMScheduler"</span>);</pre>
<pre> is4uScheduler.ExecuteCommand(OnDemand);</pre>
<pre class="alt">}</pre>
</div>
<br />
If you want to know more about developing custom activities, this <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ee652258%28v=vs.100%29.aspx">
article</a> is a good starting point.<br />
The integer value is arbitrary. You only need to make sure you send the same value as is defined in the service source code. The ServiceController takes the system name of the Windows service.
<h2>Powershell</h2>
The same is possible in Powershell:<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ -->
<div class="csharpcode">
<pre class="alt">[System.Reflection.Assembly]::Load(<span class="str">"System.ServiceProcess, </pre>
<pre> Version=2.0.0.0, Culture=neutral, </pre>
<pre class="alt"> PublicKeyToken=b03f5f7f11d50a3a"</span>)</pre>
<pre>$is4uScheduler = New-Object System.ServiceProcess.ServiceController</pre>
<pre class="alt">$is4uScheduler.Name = <span class="str">"IS4UFIMScheduler"</span></pre>
<pre>$is4uScheduler.ExecuteCommand(234)</pre>
</div>
<br />
Another extension I implemented (inspired by <a href="https://social.technet.microsoft.com/profile/dave%20nesbitt/">
Dave Nesbitt</a>'s question on my <a href="http://social.technet.microsoft.com/wiki/contents/articles/19397.how-to-use-windows-service-for-scheduling-forefront-identity-manager-fim.aspx">
previous post</a>) was the delay step. This kind of step allows you to insert a window of time between two management agent runs. This in addition to the default delay, which is inserted between every step. So now there are four kind of steps possible in the
run configuration file: LinearSequence, ParallelSequence, ManagementAgent and Delay. I saw the same idea being implemented in powershell
<a href="https://konab.com/schedule-fim-2010-sleep-option/">here</a>.<br />
<br />
A very usefull function I didn't mention in my previous post, but was already there, is the cleanup of the run history (which can become very big in a fast-synchronizing FIM deployment). This function can be enabled by setting the option "<span class="nt">ClearRunHistory</span>"
to true and setting the number of days in the "<span class="nt">KeepHistory</span>" option. If you enable this option, you need to make sure the service account running the service is a member of the FIM Sync Admins security group. If you do not use this option,
membership of the FIM Sync Operators group is sufficient.<br />
<br />
To end I would like to give you pointers to some other existing schedulers for FIM:
<br />
<a href="http://social.technet.microsoft.com/wiki/contents/articles/1899.fim-2010-how-to-automate-sync-engine-run-profile-execution.aspx">FIM 2010: How to Automate Sync Engine Run Profile Execution</a>
Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-73849521747601310692014-09-05T16:28:00.006+02:002014-09-05T16:28:53.629+02:00Setting up Oracle Access Portal Service<div class="MsoNormal">
While setting up Oracle Access Portal Service I ran into some issues, and came up with the following workarounds.<br />
<br />
<h3>
<b>Issue #1 – Configuring Web Application Templates</b></h3>
<br />
Although not very clear, the documentation states that one must use de ESSO LM Administration console to configure web application templates and then either publish them directly to the LDAP repository, or export from ESSO LM Admin Console and import them back through the OAM Administration console.<br />
<br />
None of these methods work! Once the application is published to the LDAP and you access it from the OAM admin console, the application is listed but if you try to access it the only thing that you will get is ADF exceptions. As for the export/import method, when you try to import the file from the OAM admin console, nothing happens, not even an ADF exception.<br />
<br />
With this scenario the way to configure applications is to use the ESSO LM Admin Console to configure a Web Application, then create a new application in the OAM Admin Console, replicating the application settings defined in the ESSO LM Admin Console.<br />
<br />
<h3>
<b>Issue #2 – Oracle Traffic Director Webgate</b></h3>
<br />
Oracle Access Portal Service works by injecting a javascript resource (columbiaWeb.js) into html pages, which then calls methods located in /idaas.<br />
The requests made by columbiaWeb.js to the /idaas resources were coming back with HTTP error 405 - method not allowed. The HTTP method used to request these resources is GET, and included in the response was a list of allowed methods which included every HTTP method except for GET.<br />
<br />
While investigating this issue I found that the Webgate has some hardcoded directives regarding /idaas.<br />
I realized this by executing the command: strings /WebGate_HOME/webgate/iplanet/lib/esso_webproxy.so|grep idaas<br />
This command yelds the following output:<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>/idaas/am/esso/v1/userwallet/credentials?ESSO_Payload_Request=<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>/idaas/am/esso/v1/app/policies?ESSO_Payload_Request=<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>/idaas/am/esso/v1/userwallet/credentials<br />
<br />
<br />
I tried many different approaches to solve this issue with the OTD webgate, including commenting the entry in the Oracle Traffic Director instance instance-obj.conf configuration file that pointed to the /idaas resource and getting the resource from OAM Server through other means, but was unsuccessful.<br />
<br />
Eventually the solution I came up with is to use Apache HTTP Server instead of Oracle Traffic Director.<br />
<br />
I installed and configured mod_webglogic in Apache so that I could map /idaas resources to the OAM Server and then copied OTDs columbiaWeb.js to the Apache Webgate folder /Webgate_HOME/webgate/apache/oamsso/global/.<br />
<br />
Added the following entries to APACHEs configuration files where needed, to inject columbiaWeb.js:<br />
<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>AddOutputFilterByType SUBSTITUTE text/html<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>Substitute "s|</head>|<script type='text/javascript' id='OracleSSOProxy' essoLoggingLevel='0' src='/oamsso/columbiaWeb.js' oam_partner='Webgate_IDM_11g' essobasepath='http://oap.oam.demo' essoProxyType='DNS' essoConsoleLoggingLevel='0'></script></head>|i"<br />
<br />
Added this entry to webgate.conf:<br />
<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span><LocationMatch "/idaas/*"><br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>Satisfy any<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span></LocationMatch><br />
<br />
This last entry unprotects the /idaas resource, not doing so will result in an empty json response and the following entry in the OAM server log: <ESSOTokenManager object is null. Session could not created and hence use-case can not move ahead. Returning the empty response back.><br />
<br />
<h3>
<b>Issue #3 – Dealing With Iframes</b></h3>
<br />
While configuring Gmail I realized that columbiaWeb.js was not handling iframes correctly so I created a new Javascript file based on columbiaWeb.js and after this entry:<br />
<br />
var validFrames = this.getFrames();<br />
if (validFrames[0] === null) {<br />
if (global.oracleESSO.globals.logger.enabled(5)) global.oracleESSO.globals.logger.debug("matchTemplates end; No valid frames.");<br />
return 0;<br />
}<br />
<br />
I added this piece of code so that it would ignore iframes:<br />
<br />
var validFramesTmp = [];<br />
for (i =0; i < validFrames.length; i++) {<br />
if(validFrames[i] === window) {<br />
validFramesTmp[i] = validFrames[i];<br />
}<br />
}<br />
validFrames = validFramesTmp;<br />
<br />
<br />
This separate Javascript file was created because I don't know the impact of this workaround in other configurations.<br />
<br />
<h3>
<b>Unsolved Issues</b></h3>
<br />
While adding other forms to the configuration, for example a password change form, I get the following Javascript error in every form: global.oracleESSO.templateData.templates[matchedSections[0][prop].ParentKey1] is undefined<br />
The fields get highlighted but there is no credential insertion. Hopefully this and other issues will be fixed in a future release.<br />
<br /></div>
Unknownnoreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-68800165022920567282014-06-20T09:30:00.002+02:002014-06-20T09:30:45.944+02:00SQL Query for the FIM2010 admin accountRecently, I came across a FIM2010 deployment that wasn't very well documented. My task was to review the deployment but how could I get admin access to the FIM Portal if I didn't know which account to use? The answer is in the FIMService database and this query will get it for you:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"> SELECT Distinct a.AttributeName,p.[ValueString]</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> FROM [FIMService].[fim].[ObjectValueString] as p JOIN [FIMService].[fim].[Objects] as o ON p.ObjectKey = o.ObjectKey</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> JOIN [FIMService].[fim].[BindingInternal] as a on p.AttributeKey = a.AttributeKey</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> Where o.ObjectID ='7fb2b853-24f0-4498-9534-4e10589723c4'</span><br />
<br />
Reset the password of the account and I could proceed with my review. Hope this helps.Robinhttp://www.blogger.com/profile/04219206913960609880noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-71430947120048783412014-05-16T19:20:00.001+02:002014-05-16T19:21:24.186+02:00Congratulations for iWelcomeCongratulations are in order for our Dutch colleagues of iWelcome for winning the Best Cloud Security Project European Identity Cloud Awards 2014 (that's a mouthful). The press release is posted <a href="http://www.iwelcome.com/nl/company/news/">here</a>.Robinhttp://www.blogger.com/profile/04219206913960609880noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-2538162068526893612013-08-09T13:34:00.001+02:002013-08-27T17:41:30.784+02:00Windows service for scheduling Forefront Identity Manager<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, "Courier New", Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
All FIM projects need to be able to schedule management agent runs automatically. This blogpost describes how we implemented a windows service to schedule Forefront Identity Manager synchronization service. You can find the source code and an installer at <a href="https://github.com/wim-beck/FIM-Scheduler">FIM-Scheduler.</a><br />
<h2>
<span style="font-size: small;">Basic implementation</span></h2>
We started of by implementing the management agent run functionality in C# by calling the WMI interface using the System.Management library.<br />
<br />
<!-- code formatted by http://manoli.net/csharpformat/ -->
<br />
<div class="csharpcode">
<pre class="alt">ManagementScope ms = <span class="kwrd">new</span> ManagementScope(wmiNs);</pre>
<pre><span class="kwrd">string</span> query = <span class="str">"Select * from MIIS_ManagementAgent"</span>;</pre>
<pre class="alt">SelectQuery sq = <span class="kwrd">new</span> SelectQuery(query);</pre>
<pre><span class="kwrd">using</span> (ManagementObjectSearcher mos </pre>
<pre> = <span class="kwrd">new</span> ManagementObjectSearcher(ms, sq)) {</pre>
<pre class="alt"> <span class="kwrd"> foreach</span> (ManagementObject obj <span class="kwrd">in</span> mos.Get()){</pre>
<pre> <span class="kwrd"> using</span> (ManagementObject ma = obj){</pre>
<pre class="alt"> <span class="kwrd"> object</span>[] param = <span class="kwrd">new</span> <span class="kwrd">object</span>[] {runProfile};</pre>
<pre> ma.InvokeMethod(<span class="str">"Execute"</span>,param);</pre>
<pre class="alt"> }</pre>
<pre> }</pre>
<pre class="alt">}</pre>
</div>
<br />
So far so good, but we do not want to recompile
our code every time we want to change our schedule. Therefore, we put
our scheduling configuration in an xml file and let the program read our
xml configuration. We defined two kind of objects: <i>runConfiguration </i>and <i>sequence</i>. Both objects contain one or more <i>step </i>objects. There is no difference in the definition of runConfiguration and sequence, but only runConfigurations can be used as the starting point of a schedule. A step can be one of three types:<br />
<ol>
<li>a linear sequence: all steps are executed one after the other</li>
<li>a parallel sequence: all steps are executed in parallel</li>
<li>a management agent: the run profile defined in the Action attribute is run for the management agent</li>
</ol>
As you would expect, you can define sequences of sequences of sequences... And of course, you can use a sequence in more than one runConfiguration and/or other sequence.<br />
<br />
We gave every type of step a different implementation, so the result has the following structure: <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-0EZS_cU1hgI/UgTn55nTLEI/AAAAAAAAAB0/CvZHXBkSO3E/s1600/Steps.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="198" src="http://3.bp.blogspot.com/-0EZS_cU1hgI/UgTn55nTLEI/AAAAAAAAAB0/CvZHXBkSO3E/s400/Steps.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<h2>
<span style="font-size: small;">Windows service</span></h2>
It is very easy to create a windows service with Visual Studio since it is a standart project template called, surprisingly, Windows Service.<br />
All we needed to do was add an installer so that the executable can be installed as a service using sc.exe or installUtil.exe. There are some very good tutorials on how to create a windows service, so I'll just give you the link I used: <a href="http://msdn.microsoft.com/en-us/library/zt39148a.aspx">Creating a Windows Service Application</a>.<br />
<h2>
<span style="font-size: small;">Scheduling </span></h2>
At the moment we have a program that is capable of running a given schedule and a windows service. The last issue we need to resolve is how we are going to perform the actual scheduling. Some googling gives you some options, and we chose to use the Quartz .NET library.<br />
<blockquote class="tr_bq">
<div style="font-family: Calibri; font-size: 11.0pt; margin: 0in;">
Quartz.NET is a
full-featured, open source job scheduling system that can be used from smallest
apps to large scale enterprise systems. <span lang="nl-BE">Quartz.NET
is a pure .NET library written in C# and is a port of very propular open source
Java job scheduling framework, </span><a href="http://www.quartz-scheduler.org/"><span lang="en-US">Quartz </span></a><span lang="nl-BE">. This project owes very much to
original Java project, it's father James House and the project contributors. </span></div>
</blockquote>
After some trial and error we succeeded in configuring a Quartz scheduler that read his configuration from a file and started a job. Then it was just a matter of implementing this job so that it would start the steps in the runconfiguration specified by the Quartz configuration file.<br />
<h2>
<span style="font-size: small;">Resources</span></h2>
<a href="http://msdn.microsoft.com/en-us/library/zt39148a.aspx">Creating a Windows Service Application</a><br />
<a href="http://quartznet.sourceforge.net/index.html">Quartz .NET</a><br />
<a href="https://github.com/wim-beck/FIM-Scheduler">FIM-Scheduler</a> Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-76231396215494547742013-03-04T14:16:00.001+01:002013-03-04T14:16:21.692+01:00OBUG BeNeLux Connect 2013A new edition of <a href="http://www.obug.be/">OBUG</a>'s BeNeLux Connect event is scheduled on March 26, 2013. Be sure to check out session "The Identity Management Journey" (Session 1 Track 7) in which <a href="http://www.telenet.be/">Telenet</a> and <a href="http://www.is4u.be/">IS4U</a> will present Telenet's Oracle Identity Governance success story!<br />
Feel free to <a href="http://www.obug.nl/index.php?option=com_ckforms&view=ckforms&id=10">register</a> and join the show!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-LtLbm-Xh5yQ/UTSc_yQjd8I/AAAAAAAAAAo/ndQPx35fdEI/s1600/connect_2013_s.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-LtLbm-Xh5yQ/UTSc_yQjd8I/AAAAAAAAAAo/ndQPx35fdEI/s320/connect_2013_s.jpg" /></a></div>
Barthttp://www.blogger.com/profile/15902228395959148333noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-4400587944886954792012-10-22T23:50:00.000+02:002012-10-22T23:50:26.493+02:00OpenAM Policy Agents for IIS don't support multiple sites correctlyThe other day we were experiencing the same issue as described in <a href="https://bugster.forgerock.org/jira/browse/OPENAM-308">OpenAM issue 308</a>: when using the policy agent on multiple IIS sites, the agent configuration of the site serving the first request "wins". The great thing of open source software is that you can look at the source code!<br />
<br />
We learned from the agent source code that it uses a global variable <i>readAgentConfigFile </i>to flag if it has already processed the agent configuration file or not. We had a feeling that this could be causing the issue but weren't certain until our co-worker Thomas from <a href="http://www.exsertus.be/">Exsertus</a> explained to us that if sites share an Application Pool, they run in the same process. This means that there will be only one global variable in C and C++ code per process but this variable will be shared between multiple sites and thus OpenAM policy agent instances. This explains why every OpenAM policy agent shares the "winning" configuration file.<br />
<br />
And now for the solution? We assigned every site its own Application Pool, a practice which the customer was already applying on its production IIS servers. But in my humble opinion, that's only a workaround.Robinhttp://www.blogger.com/profile/04219206913960609880noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-26812151581539623142012-06-07T11:37:00.002+02:002012-06-07T11:41:09.322+02:00Renew OCSP signing certificate<p>
In my previous post, I described on how to automate the creation of an ocsp responder configuration. This post describes on how to renew and replace the signing certificate when it is about to expire. It also replaces the signing certificate for all ocsp responder configurations.
This script automates the process, but there is still some interaction required. The user that runs the script needs to click OK a few times.
</p>
<p>
Required parameters:
</p>
<ul>
<li>
$servername: The server name (eg. OcspServer)
</li>
<li>
$signingcertificate: The DN of your signing certificate (eg. CN=ocspSigning, OU=Cert, O=Company, C=Country)
</li>
</ul>
<p>
The two parameters can be passed on the command line, hardcoded or saved in an xml file.
</p>
The first thing we need is the serial of the OCSP signing certificate that is about to expire. We get it out of the local certificate store and store the serial number in a variable.
<pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%">
<code># Save the old ocsp signing certificate in a variable
$SigningCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate
$SigningCert = ls cert:\LocalMachine\My | where {$_.Subject -eq $signingcertificate}
# Get the serial number of the old ocsp signing certificate
$serial = $SigningCert.SerialNumber
</code></pre>
We then use certutil to get the key container. This part is a little tricky since this value is stored among a lot of other information. We pull the information we need by string matching and manipulation.
<pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%">
<code># Get the keycontainer id of the old ocsp signing certificate
$key = certutil.exe -store -v My $serial | Select-String -pattern "Key Container"
$keyContainer = ($key.ToString() -replace "Key Container = ","").Trim()
</code></pre>
Once we have the key container value, we can write a certificate request file 'ocspsigigning.inf' and pass it to the certreq command. We store the result in the file 'ocspsigning.req'.
<pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%">
<code># Write the certificate request info to a file
Write-Output "[NewRequest]" | Out-File "ocspsigning.inf"
Write-Output "RenewalCert = $serial" | Out-File "ocspsigning.inf" -append
Write-Output "KeyContainer = $keyContainer" | Out-File "ocspsigning.inf" -append
Write-Output "UseExistingKeySet = True" | Out-File "ocspsigning.inf" -append
Write-Output "MachineKeySet = True" | Out-File "ocspsigning.inf" -append
# Request a renewal of the ocsp signing certificate using the constructed info
Certreq -new ocspsigning.inf ocspsigning.req
</code></pre>
We can now submit the request. We save the returned information because we need the requestID to retrieve the issued certificate. As you have seen above, the same pattern matching and string manipulation is used to obtain this value.
Finally we accept the certificate.
<pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%">
<code># Save the request id in a variable while submitting the request
$reqId = Certreq -submit ocspsigning.req ocspsigning.cer | Select-String -pattern "RequestId: [0-9]"
$reqId = ($reqId.ToString() -replace "RequestId:","").Trim()
# Retrieve the requested certificate
Certreq -retrieve $reqId ocspsigning.cer
# Store the certificate in the local certificate store
Certreq -accept -machine ocspsigning.cer
</code></pre>
Now that we have a renewed signing certificate, we can use it to sign the ocsp responses. Therefore, we need to reassign the renewed certificate to all ocsp responder configurations. We save the raw certificate data of the new certificate and iterate over all OCSP configurations in the server array.
<pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%">
<code># Save the new ocsp signing certificate in a variable
$SigningCert = ls cert:\LocalMachine\My | where {$_.Subject -eq $signingcertificate}
$SigningCert = $SigningCert.GetRawCertData()
# Assign the new ocsp signing certificate to all revocation configurations
$OcspAdmin = New-Object -com "CertAdm.OCSPAdmin"
$OcspAdmin.GetConfiguration($servername, $true)
$OcspAdmin.OCSPCAConfigurationCollection | ForEach-Object {
Write-Host "Replacing signing certificate for" $_.Identifier
$_.SigningCertificate = $SigningCert
}
# Commit the new configuration
$OcspAdmin.SetConfiguration($servername, $true)
</code></pre>
<p>
Following links were very helpful when I was writing this script:
</p>
<ul>
<li>
<a href="http://blogs.technet.com/b/configmgrteam/archive/2009/02/11/how-to-renew-the-site-server-signing-certificate-microsoft-certificate-services.aspx">How to renew the server signing certificate</a>
</li>
<li>
<a href="http://blogs.technet.com/b/configmgrteam/archive/2009/02/09/how-to-specify-the-container-name-on-the-web-enrollment-page-for-certificate-services.aspx">How to specify the key container</a>
</li>
</ul>Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-88811663252204996722012-06-07T11:32:00.001+02:002012-06-07T11:40:50.982+02:00OCSP powershell script<p>
I read following question on <a href="http://blogs.technet.com/b/askds/">http://blogs.technet.com/b/askds/</a>.
</p>
<div style="color: #000000;background-color: #eee;padding: 5px;border: 1px dashed #999999;">
<h3><a name="ocsp"></a>Question</h3>
<p>
Are there any available PowerShell, WMI, or command-line options for configuring an OCSP responder? I know that I can install the feature with the Add-WindowsFeature, but I'd like to script configuring the responder and creating the array.
</p>
<h3>Answer</h3>
<p>
<em>[Courtesy of the <a href="http://blogs.technet.com/b/askds/archive/tags/jonathan+stephens/">Jonathan “oh no, feet!” Stephens</a> – Ned]</em>
</p>
<p>
There are currently no command line tools or dedicated PowerShell cmdlets available to perform management tasks on the Online Responder. You can, however, use the COM interfaces <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa386313(v=vs.85).aspx">IOCSPAdmin</a> and <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa386328(v=vs.85).aspxhttp:/msdn.microsoft.com/en-us/library/windows/desktop/aa386328(v=vs.85).aspx">IOSCPCAConfiguration</a> to manage the revocation providers on the Online Responder.
</p>
<ol>
<li>
Create an <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa386313(v=vs.85).aspx">IOSCPAdmin</a> object.
</li>
<li>
The <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa386323(v=vs.85).aspx">IOSCPAdmin::OCSPCAConfigurationCollection</a> property will return an <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa386330(v=vs.85).aspx">IOCSPCAConfigurationCollection</a> object.
</li>
<li>
Use <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa386335(v=vs.85).aspx">IOCSPCAConfigurationCollection::CreateCAConfiguration</a> to create a new revocation provider.
</li>
<li>
Make sure you call <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa386327(v=vs.85).aspx">IOCSPAdmin::SetConfiguration</a> when finished so the online responder gets updated with the new revocation configuration.
</li>
</ol>
<p>
Because these are COM interfaces, you can call them from VBScript or PowerShell, so you have great flexibility in how you write your script.
</p>
</div>
<h3>My solution</h3>
<p>
I have written and tested a powershell script that creates an ocsp configuration.
</p>
<p>
Required parameters:
</p>
<ul>
<li>
$cert: CA certificate (stored locally)
</li>
<li>
$crl: URL of the corresponding crl
</li>
<li>
$servername: The server name (eg. OcspServer)
</li>
<li>
$signingcertificate: The DN of your signing certificate (eg. CN=ocspSigning, OU=Cert, O=Company, C=Country)
</li>
</ul>
<p>
The first two parameters can be passed on the command line, the other two can be hardcoded or saved in an xml file.
</p>
<p>
First thing we do is save the raw certifcate data of the signing certificate.
<pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%">
<code># Create a new certificate object
$SigningCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate
# Get the certificate from the local store by using it's DN
$SigningCert = ls cert:\LocalMachine\My | where {$_.Subject -eq $signingcertificate}
# Save the raw certificate data
$SigningCert = $SigningCert.GetRawCertData()
</code></pre>
Next we convert the string that points to the certificate to a file, take it's location and save the raw certificate data of the ca certificate.
<pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%">
<code>$file = Get-Childitem $cert
$certName = $file.Name
$certPath = $file.DirectoryName
$CaCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate
$CaCert.Import($certPath + "\" + $certName)
$CaCert = $CaCert.GetRawCertData()
</code></pre>
We then create an OCSPPropertyCollection. In here we store the url of the certificate revocation list and we specify the refresh interval. After the time specified in the refresh interval, windows will download the newest version of the crl from the specified location.
<pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%">
<code># Save the desired OcspProperties in a collection object
$OcspProperties = New-Object -com "CertAdm.OCSPPropertyCollection"
$OcspProperties.CreateProperty("BaseCrlUrls", $crl)
$OcspProperties.CreateProperty("RevocationErrorCode", 0)
# Sets the refresh interval to 1 hour (time is specified in milliseconds)
$OcspProperties.CreateProperty("RefreshTimeOut", 3600000)
</code></pre>
Finally, we create an OCSPAdmin object and create a new revocation provider.
<pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%">
<code># Save the baseName in a variable, this is the filename without extension
# eg. basename of certificate.cer is certificate
$certBaseName = $file.BaseName
# Save the current configuration in an OcspAdmin object
$OcspAdmin = New-Object -com "CertAdm.OCSPAdmin"
$OcspAdmin.GetConfiguration($servername, $true)
# Create a new revocation configuration
$NewConfig = $OcspAdmin.OCSPCAConfigurationCollection.CreateCAConfiguration($certBaseName, $CaCert)
$NewConfig.HashAlgorithm = "SHA1"
$NewConfig.SigningFlags = 0x020
$NewConfig.SigningCertificate = $SigningCert
$NewConfig.ProviderProperties = $OcspProperties.GetAllProperties()
$NewConfig.ProviderCLSID = "{4956d17f-88fd-4198-b287-1e6e65883b19}"
$NewConfig.ReminderDuration = 90
# Commit the new configuration to the server
$OcspAdmin.SetConfiguration($servername, $true)
</code></pre>
</p>
<p>
Following links were very helpful when I was writing this script:
</p>
<ul>
<li>
<a href="http://jorgequestforknowledge.wordpress.com/category/active-directory-certificate-services-adcs/">Managing certificates with powershell - Jorgequestforknowledge</a>
</li>
<li>
<a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa386328%28v=vs.85%29.aspx">The MSDN Reference also mentioned in the answer to the original question</a>
</li>
</ul>Wim Beckhttp://www.blogger.com/profile/02451721010743698951noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-64235855044811795912012-03-02T13:10:00.010+01:002012-03-02T14:21:30.865+01:00Boost for ForgeRockExciting news today! Our partner <a href="http://forgerock.com/">ForgeRock</a> send out a press release on securing $7M in a series A funding. Read all about it <a href="http://forgerock.com/content/forgerock-secures-7m-series-funding-accel-partners">here</a>.<br /><br />IS4U is looking forward to what end this investment will aid the evolution of ForgeRock's <a href="http://forgerock.com/strategy.html">I³ Open Identity Platform</a> which includes <a href="http://forgerock.com/openam.html">OpenAM</a>, <a href="http://forgerock.com/openidm.html">OpenIDM</a> and <a href="http://forgerock.com/opendj.html">OpenDJ</a>.Barthttp://www.blogger.com/profile/15902228395959148333noreply@blogger.comtag:blogger.com,1999:blog-698829246057037173.post-62295317362475574982011-09-08T16:15:00.013+02:002011-09-08T17:17:44.205+02:00Oracle OpenXperience 21.10.2011<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.eventserver.be/oracle/openxperience"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 149px;" src="http://3.bp.blogspot.com/-yVaNJdfS_x8/TmjOteFXVII/AAAAAAAAAAY/u_WfaGllMPk/s400/openxperience2011_head03.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5649993013101286530" /></a><br /><br />Oracle is one of the main designers of the future world of business. We offer you the unique opportunity to get a clear view on Oracle’s Roadmap for 2012.<br /><br />You are kindly invited to experience the Oracle and the Oracle Partner view on the future at the Oracle <a href="http://www.eventserver.be/oracle/openxperience">OpenXperience</a> Seminar on 21 October 2011 in Diegem (Brussels).<br /><br />IS4U will host a session on Oracle's Identity Management 11g suite, providing you with an overview on all important aspects as presented on <a href="http://www.oracle.com/openworld/index.html">Oracle OpenWorld 2011</a>.<br /><br />For more details and registration, please visit the <a href="http://www.eventserver.be/oracle/openxperience">OpenXperience</a> seminar site.<br /><br />Oracle OpenXperience is an exclusive initiative of Avnet, Contribute, HRMC, i4bi, iAdvise, iRelate, <a href="http://www.IS4U.be">IS4U</a>, RMC, Tripwire Solutions and Uptime. It is sponsored by <a href="http://www.oracle.com">Oracle</a>.Barthttp://www.blogger.com/profile/15902228395959148333noreply@blogger.com