Jekyll2015-12-10T22:49:22+00:00http://justinjahn.me/Hi, I'm Justin. I provide excellent technology solutions to meet expanding business needs. This is where I post useful tips, tricks, and all kinds of other stuff about technology.
Readahead File Parsing in Powershell2015-01-28T00:00:00+00:002015-01-28T00:00:00+00:00http://justinjahn.me/tutorials/2015/01/28/readahead-file-parsing-with-powershell<p>Powershell has quickly grown into one of my favorate scripting languages because of its flexibility and near seamless integration with the .NET framework. I use it often to do simple tasks to which parsing simple text files is one.</p>
<p>Have you ever needed to look a few lines ahead in a file before writing it’s contents out- perhaps to determine what the file should be named? Then this is the article for you!</p>
<h2 id="concept">Concept</h2>
<p>The concept is incredibly simple. We’re going to use a <a href="http://en.wikipedia.org/wiki/Queue_%28abstract_data_type%29">queue</a> to temporary store data before writing them out.</p>
<h2 id="adding-flexibility-with-parameters">Adding Flexibility With Parameters</h2>
<p>We’ll be using some parameters to allow more flexibility when running the script:</p>
<pre><code>[cmdletbinding()]
param (
[String]
[Parameter(Mandatory=$true)]
[ValidateScript({Test-Path $_ -PathType Leaf})]
$InputFile,
[String]
[Parameter(Mandatory=$true)]
[ValidateScript({Test-Path $_ -PathType Container})]
$OutputFolder
)
</code></pre>
<p>You can learn more about PowerShell parameters in this <a href="https://technet.microsoft.com/en-us/library/hh847743.aspx">technet article</a>. These parameters help make our script reusable, which is always a good thing, because you never know what might find it’s way into your work queue next!</p>
<h2 id="the-basics">The Basics</h2>
<p>We’ll be creating a <code>System.Collections.Queue</code> object to store lines that we needed to read in advance, and a <code>System.IO.StreamReader</code> to actually read those lines:</p>
<pre><code>$oQueue = New-Object System.Collections.Queue
$oFileStream = New-Object System.IO.StreamReader $InputFile
</code></pre>
<p>It may be a little mind bending at first as the usage of a queue adds an element of complexity, however it isn’t bad when broken down:</p>
<ol>
<li>We loop indefinately until the end of the input file is reached.</li>
<li>Look through each line of the file for a special marker or formatting, processing the queue as necessary.</li>
<li>Process as many lines as needed to obtain the information necessary, adding each line that was read to the queue.</li>
<li>Add any lines not necessary to process to the queue for later.</li>
<li>After our file has ended, we may still have information in the queue, and should be processed accordingly.</li>
</ol>
<p>Let’s get to the example:</p>
<pre><code>$sTitle = ""
$oOutputFile = $null
# Loop through each line of the file until we've reached the end
while (!$oFileStream.EndOfStream) {
$sCurrentLine = $oFileStream.ReadLine()
# We've found a new report, figure out a new title, and write the previous report
if ($sCurrentLine.StartsWith("-- START OF REPORT --")) {
# Make sure we have an open StreamWriter before dumping the queue.
if ($oOutputFile -ne $null) {
foreach ($line in $oQueue) {
$oOutputFile.WriteLine($line)
}
# A foreach does not clear the queue, only iterates it.
$oQueue.Clear()
}
$sNewTitle = ""
# Read ahead to the next line and set the title
$oQueue.Enqueue($sCurrentLine)
$sCurrentLine = $oInputFile.ReadLine()
$sReportTitle = ($sCurrentLine -replace "TITLE:\s?([A-Za-z0-9]+)",'$1').Trim()
# Read ahead to the next line and set the report ID
$oQueue.Enqueue($sCurrentLine)
$sCurrentLine = $oInputFile.ReadLine()
$sReportId = $sCurrentLine -replace "ID:\s?([A-Za-z0-9]+)",'$1').Trim()
# Put it all together
$sNewTitle = "{0} - {1}" -f $sReportId, $sReportTitle
# Close the old file if it exists
if ($oOutputFile -ne $null) {
$oOutputFile.Close()
}
# Create a new StreamWriter object
$sFilename = $sNewTitle + ".txt"
$oOutputFile = New-Object System.IO.StreamWriter (Join-Path $OutputFolder $sFilename)
}
$oQueue.Enqueue($sCurrentLine)
}
</code></pre>
<p>In this example, we are searching a file for a special marker, <code>-- START OF REPORT --</code>, looking for a report title, and ID. That information is then used to form a filename.</p>
<p>Notice that during this time, we also write our queue to a file if one exists. This makes the script more flexible as it allows for multiple reports in one file, but it also introduces another issue… What about the last report?!</p>
<h2 id="dealing-with-the-leftovers">Dealing With the Leftovers</h2>
<p>To deal with those pesky leftovers, we must simply write out the rest:</p>
<pre><code>if ($oOutputFile -ne $null) {
foreach ($line in $oQueue) {
$oOutputFile.WriteLine($line)
}
# A foreach does not clear the queue, only iterates it.
$oQueue.Clear()
}
</code></pre>
<h2 id="finishing-up-the-script">Finishing Up the Script</h2>
<p>It’s always good to close any open resources:</p>
<pre><code>$oOutputFile.Close()
$oFileStream.Close()
</code></pre>
<h2 id="other-reasons">Other Reasons</h2>
<p>This was just one example, but there are some other compelling reasons to queue up data before processing. One such example is to help minimize the IO impact of small reads and writes. By caching several lines of a file, writes to a hard drive can be done sequentially, making for smoother operation. It should be mentioned that the operating system probably does a better job doing this, so your mileage may vary, but it’s worth a try if performance is a priority.</p>
Powershell has quickly grown into one of my favorate scripting languages because of its flexibility and near seamless integration with the .NET framework. I use it often to do simple tasks to which parsing simple text files is one.Finding Exchange Servers Using Active Directory and Powershell2014-11-24T00:00:00+00:002014-11-24T00:00:00+00:00http://justinjahn.me/programming/2014/11/24/finding-exchange-servers-using-active-directory<p>Sometimes PowerShell scripts need to send email messages to other users, and it is pretty annoying to hard code SMTP servers if you’ve got a ton of scripts lying around across several different servers. Wouldn’t it be nice if the script could find a valid Exchange server from AD? Well, not only is this possible, it’s pretty easy as well:</p>
<script src="https://gist.github.com/justinjahn/58db206be39f100945e3.js"></script>
<p>The above Gist is just a simple example of that. If I wanted to find all hub transport servers, I can filter it quickly:</p>
<pre><code>Get-ExchangeServers | ? { $_.Roles -contains "HT" }
</code></pre>
<p>Now, you’ll never need to hard code servers in your scripts again!</p>
Sometimes PowerShell scripts need to send email messages to other users, and it is pretty annoying to hard code SMTP servers if you’ve got a ton of scripts lying around across several different servers. Wouldn’t it be nice if the script could find a valid Exchange server from AD? Well, not only is this possible, it’s pretty easy as well:Cisco 3945, EtherSwitch, and UCSE2014-08-27T00:00:00+00:002014-08-27T00:00:00+00:00http://justinjahn.me/tutorials/2014/08/27/cisco-3945-etherswitch-and-ucse<p>We’ve been working on network upgrades to our aging branch networking equipment and ran into some roadblocks when dealing with UCS-E series servers and EtherSwitch modules in a Cisco 3945E router. <a href="http://www.cisco.com/c/en/us/products/servers-unified-computing/ucs-e-series-servers/index.html">UCS-E</a> is a fantastic piece of technology whereby an Intel-based x86 server can be slotted into a Cisco router, and <a href="http://www.cisco.com/c/en/us/products/collateral/routers/3900-series-integrated-services-routers-isr/data_sheet_c78-553980.html">EtherSwitch</a> modules are full fledged switches (with optional PoE and Layer 3) that do the same. These types of modules make for an extremely dense and robust branch office environment.</p>
<p>The goal I set for the configuration of this equipment is to have the server’s CIMC and VMWare IP addresses in a <code>SERVER</code> vlan, which was also trunked to the two EtherSwitch modules.</p>
<h2 id="the-problem">The Problem</h2>
<p>The internal <code>ucse</code> interfaces operate as routed ports, and the <code>GigabitEthernet</code> interfaces for the EtherSwitch modules operate as switchports. This makes things slightly more tricky than things would be otherwise as we’ve got to somehow bridge them into the same broadcast domain.</p>
<h2 id="the-solution">The Solution</h2>
<p>By bridging the <code>ucse</code> interface and <code>vlan</code> interface together, we can place the UCS-E server into the same broadcast domain as the dot1q trunk we send to the EtherSwitch. In this guide, we will work towards this goal by creating a <em>bridged virtual interface</em> that connects the two together.</p>
<h2 id="configure-vlan-and-bvi">Configure VLAN and BVI</h2>
<p>The first step is to configure the VLAN and BVI interfaces. A <a href="http://www.cisco.com/c/en/us/support/docs/lan-switching/integrated-routing-bridging-irb/17054-741-10.html">bridged virtual interface</a> can be used to join two routed interfaces together:</p>
<pre><code>ROUTER#conf t
ROUTER(config)#vlan 8
ROUTER(config-vlan)#name SERVER
ROUTER(config-vlan)#exit
ROUTER(config)#bridge irb
ROUTER(config)#int bvi 8
ROUTER(config-if)#description VLAN 8 ROUTED
ROUTER(config-if)#ip address 10.0.8.1 255.255.255.0
ROUTER(config-if)#exit
ROUTER(config)#bridge 8 protocol ieee
ROUTER(config)#bridge 8 route ip
ROUTER(config)#int vlan 8
ROUTER(config-if)#description VLAN 8 TRUNK
ROUTER(config-if)#bridge-group 8
</code></pre>
<p>The above configuration creates <code>vlan 8</code>, an <code>interface vlan 8</code> (or <a href="https://learningnetwork.cisco.com/thread/62241">Switched Virtual Interface</a> as they are commonly called), and a bridge group called <code>bvi 8</code>. The <code>brige-group</code> command is used to join an interface to a BVI. The <code>bridge n protocol ieee</code> and <code>bridge n route ip</code> tells the Cisco router that this bridge is for IP traffic. The <code>bridge irb</code> command enabled <a href="http://www.cisco.com/c/en/us/tech/lan-switching/integrated-routing-bridging-irb/index.html">Integrated Routing & Bridging</a>. IRB must be configured before trying to create a BVI.</p>
<h2 id="configure-ucs-e">Configure UCS-E</h2>
<p>The UCS-E server has two internal ports, but we’re only focusing on one in this post: the <code>console</code> port. This is the first interface listed in the slot where your UCS-E server is installed. Mine happens to be in slot 4:</p>
<pre><code>ROUTER#conf t
ROUTER(config)#int ucse 4/0
ROUTER(config-if)#ip unnumbered BVI 8
ROUTER(config-if)#bridge-group 8
ROUTER(config-if)#imc ip address 10.0.8.2 255.255.255.0 default-gateway 10.0.8.1
ROUTER(config-if)#imc access-port shared-lom console
</code></pre>
<p>In the above configuration, we assign the router’s interface to the <code>bridge-group</code> 8, and give it the same IP address the <code>BVI 8</code> interface has. This effectively places it in <code>VLAN 8</code> because of our earlier configuration. We assign the server’s CIMC address, and tell it to use the built-in <code>console</code> port for both CIMC and server traffic.</p>
<h2 id="configure-etherswitch">Configure EtherSwitch</h2>
<p>The EtherSwitch modules must have an IP address on the internal management interface. This interface is the first one listed on the router for the slot the EtherSwitch has been installed in. We will be using <code>loopback 0</code> and the <code>ip unnumbered</code> command to make this happen as there is no need to do anything extra to get these to work. You may want to do something differently if the ability to SSH to each individual EtherSwitch is important to you, but here we’ll be connecting to the router and using the <code>service-module</code> command for the rest.</p>
<pre><code>ROUTER#conf t
ROUTER(config)#int loopback 0
ROUTER(config-if)#ip address 10.0.0.1 255.255.255.255
ROUTER(config-if)#no shutdown
</code></pre>
<p>As you can see we’ve just created a <code>loopback 0</code> and assigned it an IP. Configuring the EtherSwitch’s management interface is equally simplistic. I happened to have two of these, so the same thing was done for both (the second one was on <code>gi 2/0</code>):</p>
<pre><code>ROUTER#conf t
ROUTER(config)#int gi 1/0
ROUTER(config-if)#ip unnumbered loopback 0
ROUTER(config-if)#no shutdown
</code></pre>
<h3 id="trunking-the-vlans">Trunking the VLANs</h3>
<p>Trunking is done exactly the same way it would be done on a traditional Catalyst switch: <code>switchport mode trunk</code>. Again, I did the same thing to both EtherSwitch modules’ second interface:</p>
<pre><code>ROUTER#conf t
ROUTER(config)#int gi 1/1
ROUTER(config-if)#switchport mode trunk
ROUTER(config-if)#no shutdown
</code></pre>
<p>Now we’ll move on to the switches themselves. Again, the configuration is simple, but you’ll need to consult the Cisco documentation on which interface is the management and which is data. Ours happened to be <code>gi 0/25</code>, and I would guess that it would be <code>gi 0/49</code> on a 48 port switch module:</p>
<pre><code>ROUTER#service-module gi 1/0 session
Trying 10.0.0.1, 2066 ... Open
<enter>
<enter>
SWITCH>en
SWITCH#conf t
SWITCH(config)#vlan 8
SWITCH(config-vlan)#name SERVER
SWITCH(config-vlan)#exit
SWITCH(config)#int gi 0/25
SWITCH(config-if)#switchport trunk encap dot1q
SWITCH(config-if)#switchport mode trunk
SWITCH(config-if)#exit
SWITCH(config)#int range gi 0/1 - 24
SWITCH(config-if-range)#switchport mode access
SWITCH(config-if-range)#switchport access vlan 8
<Ctrl-Shift-6, x>
ROUTER#disconnect
Closing connection to 10.2.0.1 [confirm]
<enter>
ROUTER#
</code></pre>
<p>In the above config, we enter into the EtherSwitch, setup <code>vlan 8</code>, configure the <code>gi 0/25</code> interface to be a dot1q trunk, and then untag every interface to <code>vlan 8</code>. Note that <code>Ctrl-Shift-6, x</code> is what’s used to exit the switches’ console. Typing <code>exit</code> does exactly what it would if you were connected via a serial cable.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The <code>bvi 8</code> interface is the thing doing all of the routing in this instance, which may seem odd to those who aren’t used to bridging in the Cisco world. This guide can also serve as a solid example of how to configure EtherSwitches- there isn’t a whole lot of information floating out there for that either.</p>
<p>Something to note here is that one probably shouldn’t just trunk a single VLAN to each EtherSwitch. Creating new vlans is just as simple as it is on Catalyst switches: <code>vlan x</code> and <code>int vlan x</code>.</p>
<p>I hope this article has helped you, and if you have any questions, please feel free to ask them down below!</p>
We’ve been working on network upgrades to our aging branch networking equipment and ran into some roadblocks when dealing with UCS-E series servers and EtherSwitch modules in a Cisco 3945E router. UCS-E is a fantastic piece of technology whereby an Intel-based x86 server can be slotted into a Cisco router, and EtherSwitch modules are full fledged switches (with optional PoE and Layer 3) that do the same. These types of modules make for an extremely dense and robust branch office environment.Paginated List of Files in ASP.NET MVC2014-05-03T00:00:00+00:002014-05-03T00:00:00+00:00http://justinjahn.me/tutorials/2014/05/03/paginated-list-of-files-in-aspnet-mvc<p>I recently encountered a need to paginate a list of files and directories as some folders may contain hundreds of items and it isn’t very nice to dump hundreds of filenames into a table and call it good.</p>
<h2 id="building-a-model">Building a Model</h2>
<p>The <code>FilesystemExplorer</code> is the Model we will be handing our MVC views:</p>
<pre><code>public class FilesystemExplorer
{
public int CurrentPage { get; set; }
public int TotalItems { get; set; }
public int MaxItems { get; set; }
public List<DirectoryInfo> Directories
{
get;
private set;
}
public List<FileInfo> Files
{
get;
private set;
}
public int Count
{
get
{
return this.Directories.Count + this.Files.Count;
}
}
public int TotalPages
{
get
{
if (this.TotalItems > 0) {
return (int)Math.Ceiling(this.TotalItems / (double)this.MaxItems);
} else {
return 0;
}
}
}
public bool HasNextPage
{
get
{
return (this.CurrentPage < this.TotalPages);
}
}
public bool HasPreviousPage
{
get
{
return (this.CurrentPage > 1);
}
}
public FilesystemExplorer()
{
this.Directories = new List<DirectoryInfo>();
this.Files = new List<FileInfo>();
this.TotalItems = 0;
}
}
</code></pre>
<p>The class has some important statistics as well as some very helpful properties that will ease the pain when generating pagination links.</p>
<h2 id="filling-in-the-details">Filling in the Details</h2>
<p>So this <code>FilesystemExplorer</code> class is great and all, but it doesn’t actually do any real work for us yet. We will remedy that with a <a href="http://en.wikipedia.org/wiki/Factory_method_pattern">Factory Method</a>:</p>
<pre><code>public static FilesystemExplorer Create(string path,
int page = 1,
int count = 25,
Func<IEnumerable<FileSystemInfo>, IEnumerable<FileSystemInfo>> decorate = null)
{
if (page < 1) {
throw new ArgumentOutOfRangeException("The page must be greater than zero.");
}
if (count < 1) {
throw new ArgumentOutOfRangeException("The count must be greater than zero.");
}
if (!Directory.Exists(path)) {
throw new System.IO.DirectoryNotFoundException();
}
// Calculate some basic pagination variables. Note that pages are zero-indexed within
/// the class itself, but pagination expects it to begin at one.
FilesystemExplorer oReturn = new FilesystemExplorer();
int iPage = page - 1;
int iCount = 0;
int iSkip = iPage * count;
int iTotal = count + iSkip;
// Make sure we have a decorating function
if (decorate == null) {
decorate = d => d.OrderBy(f => f.Name);
}
DirectoryInfo oDirectory = new DirectoryInfo(path);
IEnumerable<FileSystemInfo> ieDirectory = oDirectory.EnumerateFileSystemInfos();
// Tell the return object exactly how many items are in the directory.
oReturn.TotalItems = ieDirectory.Count();
oReturn.MaxItems = count;
oReturn.CurrentPage = page;
// Separate the results into directory and file enumerators
IEnumerable<FileSystemInfo> ieDirectories = ieDirectory.Where(
info => (info is DirectoryInfo)
);
IEnumerable<FileSystemInfo> ieFiles = ieDirectory.Where(
info => (info is FileInfo)
);
foreach (FileSystemInfo info in decorate(ieDirectories)) {
iCount++;
if (iCount <= iSkip) {
continue;
}
if (iCount > iTotal) {
return oReturn;
}
oReturn.Directories.Add(info as DirectoryInfo);
}
foreach (FileSystemInfo info in decorate(ieFiles)) {
iCount++;
if (iCount <= iSkip) {
continue;
}
if (iCount > iTotal) {
return oReturn;
}
oReturn.Files.Add(info as FileInfo);
}
return oReturn;
}
</code></pre>
<p>This class looks like a mouthful, but it isn’t that bad when we break it down. The parameters are pretty obvious minus that last one: <code>Func<IEnumerable<FileSystemInfo>, IEnumerable<FileSystemInfo>> decorate</code>. I wanted to give the calling code some flexibility on how the filesystem should be queried, such as sorting by filename or access times. This parameter does just that by handing over the object just before we actually turn it into a list.</p>
<p>The first bit of code is some sanity checking to make sure the values passed in were legit. We also define a decorator if one wasn’t given to us. The next bit is where we create our <code>IEnumerable<T></code> and get the count of files within the directory. Unfortunately, this does force an enumeration, so we lose quite a bit of performance.</p>
<p>Next, we turn our original <code>IEnumerable<T></code> into two: one for files and one for directories. Windows presents a sorted list of directories before presenting any files in Explorer, so for user experience purposes, I chose to mimic that feature. We run into some inefficiencies because of this as we must loop twice to provide the same information, but sometimes usability trumps performance.</p>
<p>The logic behind the pagination is actually quite simple. Pick directories until we’ve either run out of directories, or we’ve reached our maximum count of returnable items. If we’ve run out of directories, pick files until we either run out or reach our cap.</p>
<h2 id="a-note-on-security">A Note On Security</h2>
<p>Before we get into the usage of this code, it is necessary to point out some security principals that can save you some major embarrasment if heeded. <strong>You should never provide directory listings without careful thought and consideration.</strong> It is dangerous to give people this information as they will use it against you in any way they can. In addition, <strong>always perform input sanitization on anything coming from the user and/or the user’s browser!</strong> This includes making sure that the user cannot escape the specified root of your listing by using <code>..\ </code> or the like. I’ve used code similar to this to provide listings on internal, pasword-protected sites to reduce attack surface, but never on a public facing site. It is recommended you do the same.</p>
<h2 id="usage">Usage</h2>
<p>Now that my security rant is out of the way, let’s dive in and see how it all comes together:</p>
<pre><code>public ActionResult Index(int page, int count, string directory)
{
bool bDirectory = false;
string sBasePath = Path.GetFullPath(Properties.Settings.Default.browsePath);
string sFullPath = directory.Trim(new char[] { '\\', '/' });
// Combine the directory from our parameter with the base path.
sFullPath = Path.GetFullPath(Path.Combine(sBasePath, sFullPath));
// Make sure that the user isn't trying anything sneaky
if (!sFullPath.StartsWith(sBasePath, true, CultureInfo.CurrentCulture)) {
throw new HttpException(500, "Attempt to escape base path failed.");
}
// Sanity check to make sure we have a real directory
if (Directory.Exists(sFullPath)) {
bDirectory = true;
}
// Sanity check to make sure we have a real file.
if (!bDirectory && !System.IO.File.Exists(sFullPath)) {
throw new HttpException(404, "File Not Found");
}
// Order by write time in subdirectories, and use default ordering in base directory.
FilesystemExplorer oExplorer;
if (sFullPath == sBasePath) {
oExplorer = FilesystemExplorer.Create(sFullPath, page, count);
} else {
oExplorer = FilesystemExplorer.Create(sFullPath, page, count, i => i.OrderByDescending(f => f.LastWriteTime));
}
// Give the view a model
return View(oExplorer);
}
</code></pre>
<p>As you can see, the majority of this code is dedicated to making sure the user is not only acessing a valid directory, but making sure it is a subdirectory within our base path (or the base path itself). I made a setting, <code>browsePath</code> to keep things configurable. The object is passed to the view as a model, and likely be thrown into a table or list of some sort.</p>
<h3 id="routing">Routing</h3>
<pre><code> routes.MapRoute(
name: "Explorer",
url: "browse/{page}/{count}/{*directory}",
defaults: new { controller = "Explorer", action = "Index", page = 1, count = 25, directory = @"/" }
);
</code></pre>
<p>The route gives defaults for everything, including the directory so one can begin with simply <code>/browse</code>.</p>
<h2 id="pagination">Pagination</h2>
<p>I will be releasing an article soon about how to paginate this (and other) objects. Stay tuned!</p>
<p>As always, if you have any questions, please feel free to comment!</p>
I recently encountered a need to paginate a list of files and directories as some folders may contain hundreds of items and it isn’t very nice to dump hundreds of filenames into a table and call it good.Plugin Aware Ribbon in C# Using MEF2014-04-16T00:00:00+00:002014-04-16T00:00:00+00:00http://justinjahn.me/tutorials/2014/04/16/plugin-aware-ribbon-in-csharp-using-mef<p>With the release of .NET 4.5 came a built-in WPF ribbon control to be used by developers everywhere. I couldn’t manage to find any solid articles on how to allow plugin authors to dynamically add items to the ribbon, so I thought I’d chime in with my solution.</p>
<p>In this article, we will use the <a href="http://msdn.microsoft.com/en-us/library/dd460648(v=vs.110).aspx">Managed Extensibility Framework</a> to do the grunt work of finding our plugins and extend the <code>System.Windows.Controls.Ribbon.Ribbon</code> object to handle those plugins.</p>
<h2 id="expectations">Expectations</h2>
<p>As this is not a tutorial on MEF, I’ll expect that you have at least some knowledge of this technology. There are many excellent articles online that already cover the subject, so it didn’t seem necessary to touch on it’s intricacies here.</p>
<p>It is also helpful to have a farmiliarity with Linq as it’s used quite often in the code examples.</p>
<h2 id="overview">Overview</h2>
<p>It is important to understand how a <code>Ribbon</code> object works before we go messing around with extending it. The ribbon is essentially a system of nested <code>ItemsControl</code> objects:</p>
<ul>
<li><code>Ribbon</code>
<ul>
<li><code>ApplicationMenu</code></li>
<li><code>QuickAccessToolBar</code></li>
<li><code>RibbonTab</code>
<ul>
<li><code>RibbonGroup</code>
<ul>
<li><code>RibbonButton</code></li>
<li>…</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Knowing this, we can deduce that one adds the ribbon controls themselves to the <code>RibbonGroup</code> object’s <code>Items</code> collection, which is then placed into <code>RibbonTab.Items</code>, and finally all of the tabs are added to the <code>Ribbon.Items</code> collection.</p>
<p>The ribbon also contains two more collections, <code>ApplicationMenu</code> and <code>QuickAccessToolBar</code>, which is where we add our other buttons.</p>
<h2 id="mef-helper-class">MEF Helper Class</h2>
<p>To save some time, i’ve created a quick <a href="http://en.wikipedia.org/wiki/Singleton_pattern">singleton</a> to handle the MEF container:</p>
<pre><code>using System.ComponentModel.Composition.Hosting;
using System.Reflection;
public class MEFHelper
{
private static MEFHelper instance;
public static MEFHelper Instance
{
get
{
if (instance == null) {
instance = new MEFHelper();
}
return instance;
}
}
private AggregateCatalog catalog = new AggregateCatalog();
public AggregateCatalog Catalog
{
get
{
return this.catalog;
}
}
public CompositionContainer Container
{
get;
private set;
}
private MEFHelper()
{
this.Catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
this.Container = new CompositionContainer(this.Catalog);
}
}
</code></pre>
<h2 id="empty-interfaces">Empty Interfaces?</h2>
<p>The approach we will be using is to create <a href="http://en.wikipedia.org/wiki/Marker_interface_pattern">marker interfaces</a> (or <a href="http://en.wikipedia.org/wiki/Design_by_contract">contracts</a> as they are known in the decoupled world) that will allow MEF to see which objects it should be importing into our custom ribbon. These interfaces don’t do anything other than act pretty:</p>
<pre><code>using System;
using System.ComponentModel.Composition;
namespace MEFRibbon.Common {
[InheritedExport(typeof(IRibbonItem))]
public interface IRibbonItem { }
[InheritedExport(typeof(IRibbonMenuItem))]
public interface IRibbonMenuItem { }
[InheritedExport(typeof(IRibbonQuickItem))]
public interface IRibbonQuickItem { }
}
</code></pre>
<p>As you can see, each interface has a special MEF attribute called <a href="http://msdn.microsoft.com/en-us/library/system.componentmodel.composition.inheritedexportattribute(v=vs.110).aspx">InheritedExport</a> which tells MEF that every object which implements this one is exported.</p>
<p>I should point out that it’s always a good idea to place each class/interface/enum/whatever in it’s own file. It makes things easier to find and cleaner in general. Since this is just an example, i’ve chosen to compact things.</p>
<h2 id="dynamic-tabs-and-groups">Dynamic Tabs and Groups</h2>
<p>We could certainly take the route of adding more contracts to the pile, but wouldn’t it be nice if <code>IRibbonItem</code> objects could just tell us which tab and group they belong to?</p>
<p>To do this, we will create a custom <a href="http://msdn.microsoft.com/en-us/library/system.componentmodel.composition.exportmetadataattribute(v=vs.110).aspx">ExportMetadataAttribute</a> that will allow our ribbon to dynamically create tabs and groups as needed:</p>
<pre><code>using System;
using System.ComponentModel.Composition;
namespace MEFRibbon.Common
{
public interface IRibbonLocationAttribute
{
string TabName { get; }
string GroupName { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=false)]
public sealed class RibbonLocation : ExportAttribute, IRibbonLocationAttribute
{
public string TabName
{
get;
private set;
}
public string GroupName
{
get;
private set;
}
public RibbonLocation(string tabName, string groupName)
: base(typeof(IRibbonItem))
{
this.TabName = tabName;
this.GroupName = groupName;
}
}
}
</code></pre>
<p>As you can see, the class is pretty straightforward. The only things I can think to point out is the <code>base(typeof(IRibbonItem))</code> line and the <code>AttributeUsage</code> attribute. The first one is important because it tells MEF the type of export this attribute should be tied to, and <code>AttributeUsage</code> defines how the <code>ExportAttribute</code> should function and what may actually use it (<code>Class</code> in our case).</p>
<h2 id="finally-the-ribbon">Finally, the Ribbon!</h2>
<p>Now that the groundwork has been laid, we have everything we need to build our awesome pluggable ribbon. The code is surprisingly simple too, but I’ll break it down to try and explain things a bit better:</p>
<pre><code>using MEFRibbon.Common;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Windows.Controls.Ribbon;
using System.Linq;
using System.Linq.Expressions;
using System.Windows;
using System;
namespace MEFRibbon.Controls
{
public class MEFRibbon : Ribbon
{
[ImportMany]
private IEnumerable<Lazy<IRibbonItem, IRibbonLocationAttribute>> ribbonItems = null;
[ImportMany]
private IEnumerable<IRibbonMenuItem> ribbonMenuItems = null;
[ImportMany]
private IEnumerable<IRibbonQuickItem> ribbonQuickItems = null;
public MEFRibbon() : base()
{
MEFHelper.Instance.Container.ComposeParts(this);
this.Loaded += MEFRibbon_Loaded;
}
}
}
</code></pre>
<p>Here, we’ve got the foundation of our ribbon; three <a href="http://msdn.microsoft.com/en-us/library/vstudio/9eekhta0(v=vs.100).aspx">IEnumerable</a> fields and a constructor with some fancy looking <code>ImportMany</code> attributes. In the constructor, we see our <code>MEFHelper</code> object in action and an event hook called <code>MEFRibbon_Loaded</code>. Once <code>ComposeParts</code> is called, the <code>IEnumberable</code> fields will be filled with any plugins found by MEF.</p>
<p>Note that <code>ribbonItems</code> has a different type than the rest: <code>IEnumberable<Lazy<IRibbonItem, IRibbonLocationAttribute>></code>. While I didn’t necessarilly want to lazy load the menu items here, it’s the only way I know of to fetch the custom attribute information (that second parameter within the <code>Lazy<></code> object.</p>
<p>Moving on, we will take a look at where the magic happens:</p>
<pre><code>void MEFRibbon_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
ribbonMenuItems.ToList()
.ForEach(x => this.ApplicationMenu.Items.Add(x));
ribbonQuickItems.ToList()
.ForEach(x => this.QuickAccessToolBar.Items.Add(x));
ribbonItems.ToList().ForEach(item => {
RibbonTab oTab = this.FetchOrCreateTab(item.Metadata.TabName);
RibbonGroup oGroup = this.FetchOrCreateGroup(oTab, item.Metadata.GroupName);
oGroup.Items.Add(item.Value);
});
}
</code></pre>
<p>Once the XAML has been parsed and everything is ready, the Loaded event fires. We then use <a href="http://msdn.microsoft.com/en-us/library/bb397926.aspx">Linq</a> to loop through each plugin found, adding it to the respective sections of our object. The names are fairly self-explainatory here, so i’ll spare you the details, but let’s touch on <code>ribbonsItems</code> a bit more.</p>
<p>The <code>ribbonItems</code> field contains our buttons, checkboxes, and any other type of control we want to go on the tool strip itself. Since we are using dynamically created tabs and groups, it was necessary to create some methods to deal with that:</p>
<pre><code>private RibbonTab FetchOrCreateTab(string name)
{
// Fetch the RibbonTab object with the correct header
IEnumerable<RibbonTab> oTabs = this.Items
.Cast<RibbonTab>()
.Where(tab => tab.Header.ToString() == name);
if (oTabs.Count() > 0) {
return oTabs.First();
}
// We need a new tab.
RibbonTab oTab = new RibbonTab();
oTab.Header = name;
this.Items.Add(oTab);
return oTab;
}
private RibbonGroup FetchOrCreateGroup(RibbonTab oTab, string name)
{
IEnumerable<RibbonGroup> oGroups = oTab.Items
.Cast<RibbonGroup>()
.Where(group => group.Header.ToString() == name);
if (oGroups.Count() > 0) {
return oGroups.First();
}
// We need a new group
RibbonGroup oGroup = new RibbonGroup();
oGroup.Header = name;
oTab.Items.Add(oGroup);
return oGroup;
}
</code></pre>
<p>Some more Linq magic was used to loop through the list of tabs and groups to see if one already exists. The <code>Header</code> property is what we’ll use as the basis for a match.</p>
<p>If one doesn’t exist, we simply make one with the <code>TabName</code> and/or <code>GroupName</code> specified in the <code>IRibbonLocationAttribute</code>.</p>
<h2 id="examples">Examples</h2>
<p>Below are a few examples of how the code for each type of ribbon control will look.</p>
<h3 id="buttons">Buttons</h3>
<p>Copy.xaml:</p>
<pre><code><RibbonButton
x:Class="MEFRibbon.RibbonItems.Buttons.Copy"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Label="Copy"
Click="Copy_Click" />
</code></pre>
<p>Copy.xaml.cs:</p>
<pre><code>using MEFRibbon.Common;
using System.ComponentModel.Composition;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Ribbon;
namespace MEFRibbon.RibbonItems.Buttons
{
[RibbonLocation("Main", "Text Manipulation")]
public partial class Copy : RibbonButton, IRibbonItem
{
public Copy()
{
InitializeComponent();
}
private void Copy_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Nothing was actually copied!");
}
}
}
</code></pre>
<p>Note that we provided a <code>RibbonLocation</code> attribute at the top of the class.</p>
<h3 id="quick-launch">Quick Launch</h3>
<p>Undo.xaml:</p>
<pre><code><RibbonButton x:Class="MEFRibbon.RibbonItems.QuickLaunch.Undo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Label="Undo"
Click="Undo_Click" />
</code></pre>
<p>Undo.xaml.cs:</p>
<pre><code>using MEFRibbon.Common;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Ribbon;
namespace MEFRibbon.RibbonItems.QuickLaunch
{
public partial class Undo : RibbonButton, IRibbonQuickItem
{
public Undo()
{
InitializeComponent();
}
private void Undo_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Nothing was undone!");
}
}
}
</code></pre>
<h3 id="menu-items">Menu Items</h3>
<p>Help.xaml:</p>
<pre><code><RibbonApplicationMenuItem
x:Class="MEFRibbon.RibbonItems.ApplicationMenu.HelpApplicationMenuItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Header="_Help"
Click="Help_Click" />
</code></pre>
<p>Help.xaml.cs:</p>
<pre><code>using MEFRibbon.Common;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Ribbon;
namespace MEFRibbon.RibbonItems.ApplicationMenu
{
public partial class HelpApplicationMenuItem : RibbonApplicationMenuItem, IRibbonMenuItem
{
public HelpApplicationMenuItem()
{
InitializeComponent();
}
private void Help_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("You've been helped!");
}
}
}
</code></pre>
<h2 id="summary">Summary</h2>
<p>All in all, it isn’t that difficult to implement a custom ribbon that is plugin aware. .NET has a ton of great tools to accomplish this task in many different ways. This seemed like a solid solution because it emphasizes the use of XAML instead of a bunch of potentially messy code.</p>
<p>You can get a fully commented copy of the code I talked about in this article <a href="https://drive.google.com/file/d/0BzQtSJoIqST4YjlIbU5oLXBTUnc/">here</a>. I hope it helped you!</p>
With the release of .NET 4.5 came a built-in WPF ribbon control to be used by developers everywhere. I couldn’t manage to find any solid articles on how to allow plugin authors to dynamically add items to the ribbon, so I thought I’d chime in with my solution.WebDAV in SSIS2014-04-09T00:00:00+00:002014-04-09T00:00:00+00:00http://justinjahn.me/programming/2014/04/09/webdav-in-ssis<p>I was recently working on a business intelligence project that required pulling files from a Sharepoint document library. The initial decision was to <code>net use</code> the share, however I quickly realized that it wouldn’t work correctly when run from Windows Server.</p>
<p>I had two choices to resolve this issue:</p>
<ol>
<li>Install the <code>Desktop Experience</code> feature.</li>
<li>Use a custom <code>script task</code> to pull the file.</li>
</ol>
<p>I opted for the latter because I prefer not installing unnecessary junk on my production servers. Since .NET is already installed, why not use it?</p>
<p>WebDAV is a simple enough standard to implement as it uses GET and PUT methods to do the file transfers so I wrote a simple class to do just that.</p>
<p>The code was a bit too long to just throw in the page, but I put it in a <a href="https://gist.github.com/justinjahn/10338844">gist</a> for your pleasure. Feel free to use it however you please.</p>
I was recently working on a business intelligence project that required pulling files from a Sharepoint document library. The initial decision was to net use the share, however I quickly realized that it wouldn’t work correctly when run from Windows Server.Fast CSV Parsing With C#2014-04-09T00:00:00+00:002014-04-09T00:00:00+00:00http://justinjahn.me/programming/2014/04/09/fast-csv-parsing-in-csharp<p>When dealing with SQL Server Integration Services (SSIS), it is often difficult to add third party libraries to script tasks. It is also practically impossible to import CSV files with bad rows using the built in Flat File Data Source. So, I decided to write a custom script to import the file manually and filter out any rows with an invalid number of fields.</p>
<pre><code>/// <summary>
/// Parse a single line in CSV format.
///
/// This method uses a character looping in lieu of String.Split for performance. You could
/// squeeze more performance out of it by using .NET 4 MemoryMappedFiles for large reads and
/// possibly pointer arithmatic instead of native C# looping. Using LinkedList may also help
/// for really large lines.
/// </summary>
/// <param name="line">The line to parse.</param>
/// <param name="delimiter">The delimiter that separates each value. Usually a comma.</param>
/// <param name="qualifier">The encapsulation character of a value. Usually a double quote.</param>
/// <returns>A list of strings.</returns>
public List<string> ParseCSV(string line, char delimiter, char qualifier)
{
// The character buffer length
int iBufferLength = 512;
// The return list
List<string> oReturn = new List<string>();
// Loop through each character
char cCurrent;
bool bEncapsulated = false;
char[] acBuffer = new char[iBufferLength];
int iBuffer = 0;
for (int i = 0; i < line.Length; i++) {
cCurrent = line[i];
if (cCurrent.Equals(qualifier)) {
bEncapsulated = !bEncapsulated;
continue;
}
if (cCurrent.Equals(delimiter) && !bEncapsulated || line.Length == (i + 1)) {
if (line.Length == (i + 1)) {
acBuffer[iBuffer] = cCurrent;
iBuffer++;
}
oReturn.Add(new string(acBuffer, 0, iBuffer));
Array.Clear(acBuffer, 0, iBufferLength);
iBuffer = 0;
continue;
}
acBuffer[iBuffer] = cCurrent;
iBuffer++;
}
return oReturn;
}
</code></pre>
<p>I’ve found that the typical approach to parsing a CSV file involves string splitting. While this method tends to be easiest, it is certainly not performant and usually doesn’t take into account encapsulated fields (fields surrounded by quotes because they have commas of their own). Instead, I chose to do things at a lower level and parse the line character by character into a buffer.</p>
<p><strong>Please note that if you have fields with values longer than 512, this script will throw an <code>IndexOutOfRangeException</code>!</strong> You can increase this value in the first line of the method’s body.</p>
<p>I only did some basic benchmarking on this code, but I found that it was on average 40% faster than the string split method when parsing 100,000 rows.</p>
<p>Feel free to use it however you’d like. If you have any suggestions on how to improve this code, please comment on this post or the Gist itself. I’d love to know your opinions!</p>
When dealing with SQL Server Integration Services (SSIS), it is often difficult to add third party libraries to script tasks. It is also practically impossible to import CSV files with bad rows using the built in Flat File Data Source. So, I decided to write a custom script to import the file manually and filter out any rows with an invalid number of fields.Active Directory Authentication in RedHat2014-04-07T00:00:00+00:002014-04-07T00:00:00+00:00http://justinjahn.me/tutorials/2014/04/07/active-directory-authentication-in-redhat<p>A while ago, I wrote a <a href="https://gist.github.com/justinjahn/1323721">Gist</a> about how to bind a Red Hat or CentOS server to a Windows domain. I figured it I would feature it here on the blog as well.</p>
<h2 id="winbind">Winbind</h2>
<p>The process of linking Linux with Winbind has become increasingly straightforward, but there are some redundant pieces involved.</p>
<h3 id="installing-dependencies">Installing Dependencies</h3>
<p>We need to make sure winbind and friends are installed so there are no issues later on down the line:</p>
<pre><code>yum install -y samba-common samba-winbind-clients samba-winbind nscd ntp ntpdate
</code></pre>
<h2 id="updating-the-time">Updating the Time</h2>
<p>Keeping in sync with time is very important in an AD environment. Without the correct time, a machine will not even be able to join the domain.</p>
<pre><code>ntpdate pool.ntp.org
/sbin/chkconfig --level 35 ntpd on
/sbin/service ntpd start
</code></pre>
<h2 id="authconfig">Authconfig</h2>
<p>Authconfig is Red Hat’s utility for configuring PAM authentication. We will utilize it to do the bulk of our configuration for us. Run <code>authconfig-tui</code> and select Winbind for both <code>User Information</code> and <code>Authentiction</code>. Also, <code>Cache Information</code> should be checked. <strong>Make sure that <code>local authorization is sufficient</code> is selected in case there are problems!</strong> Do not select <code>Join Domain</code> in this dialog, as it will be done later.</p>
<ul>
<li>Security Model: ads</li>
<li>Domain: The <code>WORKGROUP</code> of your domain.</li>
<li>Domain Controllers: Specified in <code>{server}</code></li>
<li>ADS Realm: <code>{DOMAIN}</code></li>
<li>Template Shell: <code>/bin/bash</code></li>
</ul>
<p>We should also enable home directory auto creation, unless special provisioning for such things is already in place (shared filesystem for example):</p>
<pre><code>authconfig --enablemkhomedir --update
</code></pre>
<h2 id="kerberos-configuration">Kerberos Configuration</h2>
<p><strong>The authconfig script sets this up for us, but we want to verify the script did it’s job correctly.</strong></p>
<p>The Kerberos configuration file is used to join the machine to the domain as well as to reference it later on. I’ve provided an example configuration file, however some of the items need to be replaced with customized values:</p>
<ul>
<li><code>{DOMAIN}</code> Your AD domain in all caps.</li>
<li><code>{domainname}</code> Your AD domain in all lowercase.</li>
<li><code>{server}</code> The IP address of your DC.</li>
</ul>
<h3 id="example">Example</h3>
<pre><code>vi /etc/krb5.conf
[libdefaults]
default_realm = {DOMAIN}
[realms]
{DOMAIN} = {
admin_server = {server}
default_domain = {domainname}
kdc = {server}
}
[domain_realm]
{domainname} = {DOMAIN}
.{domainname} = {DOMAIN}
</code></pre>
<h2 id="samba-configuration">Samba Configuration</h2>
<p>Authconfig sets up the <code>smb.conf</code> file as well, but a few things must be changed for a easier to use solution. There is one variable in the example configuration that must be changed to suit your environment:</p>
<ul>
<li>{name} The base hostname of the system. In the case of test.example.com, it’s TEST.</li>
</ul>
<h3 id="example-1">Example</h3>
<pre><code>vi /etc/smb.conf
[global]
netbios name = {name}
winbind use default domain = true
add machine script = /usr/sbin/useradd -s /sbin/nologin -M %u
[homes]
valid users = %S
</code></pre>
<h2 id="connecting-to-active-directory">Connecting to Active Directory</h2>
<p>Now comes the fun part, connecting our server with Active Directory, joining it to the domain.</p>
<p>We will then need to add a user account that is linked to the value set in <code>{name}</code>.</p>
<pre><code>useradd -s /sbin/nologin -M {name}$
passwd -l {name}$
</code></pre>
<p>Finally, join the machine to the domain. To do this, you need access to an account with enough permissions (Usually a member of the <code>Domain Admins</code> group).</p>
<pre><code>net join -w {domain} -S {server} -U {user}
</code></pre>
<h2 id="pam">PAM</h2>
<p>From here, we must configure PAM itself. There are two main files to modify here, <code>password-auth</code>, which handles SSHd (and a few others) authentication, and <code>system-auth</code>, which deals with everything else.</p>
<p>Authconfig makes this pretty easy to do, but if not done right, modifications will be overwritten if something is changed. The good news is, this can be solved via changing the symlink from <code>system-auth-ac</code> to a custom file, <code>system-auth-custom</code> and using some include statements to link back to <code>system-auth-ac</code>:</p>
<h3 id="password-auth-custom">password-auth-custom</h3>
<pre><code># This file is designed to be used with authconfig. It adds functionality
# while still allowing authconfig to generate configuration. The symlink
# password-auth -> password-auth-ac was replaced with this file.
# The listsep argument is important as Windows groups may contain spaces
account required pam_access.so listsep=,
# Include authconfig file
auth include password-auth-ac
account include password-auth-ac
password include password-auth-ac
session include password-auth-ac
</code></pre>
<h3 id="system-auth-custom">system-auth-custom</h3>
<pre><code># This file is designed to be used with authconfig. It adds functionality
# while still allowing authconfig to generate configuration. The symlink
# system-auth -> system-auth-ac was replaced with this file.
# The listsep argument is important as Windows groups may contain spaces
account required pam_access.so debug listsep=,
# Include authconfig file
auth include system-auth-ac
account include system-auth-ac
password include system-auth-ac
session include system-auth-ac
</code></pre>
<h3 id="changing-the-symlinks">Changing the Symlinks</h3>
<pre><code>rm system-auth password-auth
ln -s system-auth-custom system-auth
ln -s password-auth-custom password-auth
</code></pre>
<p>Now we’ve got a custom authentication piece that won’t be overwritten when <code>authconfig</code> does it’s magic!</p>
<h2 id="configuring-authorization-with-pamaccessso">Configuring Authorization With pam_access.so</h2>
<p>Because of the previous step, the contents of <code>/etc/security/access.conf</code> are no longer valid as they assume lists are separated with spaces. Since Active Directory allows spaces in group names, (and encourages it), we’ve changed it to a comma.</p>
<p>To allow only members of <code>Domain Admins</code>, and <code>wheel</code> access to the system we would do the following:</p>
<pre><code>vi /etc/security/access.conf
+:root,Domain Admins,wheel:ALL
-:ALL:ALL
</code></pre>
<h2 id="sudoers">Sudoers</h2>
<p>Well, <code>Domain Admins</code> can log in, but they don’t have any privileges. How do we fix that? Sudoers of course! <strong>Warning:</strong> Whitespace characters must be escaped in the sudoers file or a syntax error will be thrown:</p>
<pre><code>visudo
%domain\ admins ALL=(ALL) ALL
</code></pre>
A while ago, I wrote a Gist about how to bind a Red Hat or CentOS server to a Windows domain. I figured it I would feature it here on the blog as well.