<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[PowerShell - Greg Shackles]]></title><description><![CDATA[PowerShell - Greg Shackles]]></description><link>https://gregshackles.com/</link><image><url>https://gregshackles.com/favicon.png</url><title>PowerShell - Greg Shackles</title><link>https://gregshackles.com/</link></image><generator>Ghost 4.32</generator><lastBuildDate>Mon, 08 Jun 2026 04:51:44 GMT</lastBuildDate><atom:link href="https://gregshackles.com/tag/powershell/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Consuming iCalendar Events with PowerShell]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>One of the cool benefits you get with PowerShell is the ability to make use of external .NET assemblies. Lately I found myself wanting to do some scripting involving an iCalendar feed, and since I hate writing console applications every time I have some small task to perform, PowerShell seemed</p>]]></description><link>https://gregshackles.com/consuming-icalendar-events-with-powershell/</link><guid isPermaLink="false">61ce48a0437e8200017d40fa</guid><category><![CDATA[PowerShell]]></category><category><![CDATA[Google Charts]]></category><category><![CDATA[iCalendar]]></category><dc:creator><![CDATA[Greg Shackles]]></dc:creator><pubDate>Sun, 27 Jun 2010 13:00:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>One of the cool benefits you get with PowerShell is the ability to make use of external .NET assemblies. Lately I found myself wanting to do some scripting involving an iCalendar feed, and since I hate writing console applications every time I have some small task to perform, PowerShell seemed like a good choice. While there is no built-in support for consuming iCalendar files, the ability to augment it with an assembly meant that I could import the <a href="http://www.ddaysoftware.com/Pages/Projects/DDay.iCal/">DDay.iCal library</a> I&#x2019;ve used before in other .NET projects.</p>
<p>For this example, I had a calendar I was using in Google Calendar to track my weight. Eventually it just became a long list of numbers that would be far more interesting if they were charted. This seemed like a good chance to give the <a href="http://code.google.com/apis/charttools/index.html">Google Chart API</a> a spin as well.</p>
<p>So let&#x2019;s get started. Any assemblies loaded into the .NET GAC will automatically be made available in PowerShell. For anything else, you can load it via the DLL:</p>
<pre><code>[Reflection.Assembly]::LoadFile(&quot;path\\to\\DDay.iCal.dll&quot;)
</code></pre>
<p>And just like that, we now have full iCalendar access from our script. Now it&#x2019;s time to load up the calendar and process it, sorting the events chronologically.</p>
<pre><code>$calendar = [DDay.iCal.iCalendar]::LoadFromUri(&quot;http://path/to/your/calendar&quot;)[0]
$sortedEvents = $calendar.Events | Sort-Object @{Expression={$_.DTStart.Value}; Ascending=$true}
</code></pre>
<p>I won&#x2019;t go into much detail as far as how the Google charts work so I&#x2019;ll just go through my (very simplistic) implementation pretty quickly. The basic idea is that I wanted to insert blank values for any dates where I didn&#x2019;t have a value, so the chart would more accurately represent the time. Since it would be messy to throw a date label on every point, I decided to just label the first of every month. Here&#x2019;s the function to do that:</p>
<pre><code>function GetLabelFromDate($date)
{
    if ($date.Day -ne 1)
    {
        return &quot;&quot;
    }

    $date.ToString(&quot;MMMM&quot;)
}
</code></pre>
<p>Next up, we&#x2019;ll prepare the chart values and labels. Since the simpler options for Google Charts impose limits on y-axis values and I had no real need to do anything more complicated, I use my own axis minimum and maximum, and scale the values accordingly.</p>
<pre><code>$values = @()
$labels = @()
$chartMin = 190
$chartMax = 220;
$valueModifier = 100 / ($chartMax - $chartMin)

# process the events in the calendar
foreach ($event in $sortedEvents)
{
    $currentDate = $event.DTStart.Value

    $currentValue = ($event.Summary - $chartMin) * $valueModifier

    if ($lastDate)
    {
        # if there&apos;s a gap in the dates, fill it in with empty data
        while ($lastDate.AddDays(1) -lt $currentDate)
        {
            $lastDate = $lastDate.AddDays(1)

            $values += 0
            $labels += GetLabelFromDate $lastDate
        }
    }

    $labels += GetLabelFromDate $currentdate
    $values += $currentValue

    $lastDate = $currentDate
}

# figure out the size of the chart and bars
$chartWidth = 800
$chartHeight = 350
$barWidth = [Math]::Floor($chartWidth / $values.length)
</code></pre>
<p>Now we have the values and labels, so all we need is a chart to put them on. To do that, we need to construct the URL. Refer to the Google Charts API for documentation on what the specifics mean.</p>
<pre><code># Reference: http://code.google.com/apis/chart/docs/gallery/bar_charts.html
$url = &quot;http://chart.apis.google.com/chart?cht=bvg&amp;chs=$($chartWidth)x$($chartHeight)&amp;chbh=$($barWidth),0,1&amp;chd=t:$([String]::Join(&quot;,&quot;, $values))&amp;chco=cc0000&amp;chxt=x,y,r&amp;chxl=0:$([String]::Join(&quot;|&quot;, $labels))&amp;chxr=1,$($chartMin),$($chartMax)&quot;
</code></pre>
<p>To download and display the chart, we&#x2019;ll use the standard .NET WebClient class to do the heavy lifting, and download the file into my temp folder. Once it&#x2019;s downloaded, the Invoke-Item cmdlet will use the default program assigned to PNG files to open it, which in my case is just the usual Windows Photo Viewer.</p>
<pre><code>$filename = &quot;$env:temp\weight-chart.png&quot;
$webClient = new-object System.Net.WebClient
$Webclient.DownloadFile($url, $filename)

Invoke-Item $filename
</code></pre>
<p><img src="https://gregshackles.com/content/images/2014/12/powershell-calendar-chart.png" alt="chart output" loading="lazy"></p>
<p>Now we have a script that will load a calendar, process the data, and chart it. Of course that&#x2019;s not all the DDay.iCalendar library is capable of, or any other library you feel like importing into your script. This is just one example of how you can use existing libraries in new and interesting ways.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Scripting IIS7 with PowerShell]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Often I find that the last thing I want to look at is yet another new language to learn, but PowerShell is proving itself to be very useful for writing up Windows scripts, particularly when you want to interact directly with Microsoft services. In my project I have several web</p>]]></description><link>https://gregshackles.com/scripting-iis7-with-powershell/</link><guid isPermaLink="false">61ce48a0437e8200017d40f5</guid><category><![CDATA[IIS]]></category><category><![CDATA[PowerShell]]></category><category><![CDATA[Scripting]]></category><dc:creator><![CDATA[Greg Shackles]]></dc:creator><pubDate>Thu, 28 Jan 2010 14:00:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Often I find that the last thing I want to look at is yet another new language to learn, but PowerShell is proving itself to be very useful for writing up Windows scripts, particularly when you want to interact directly with Microsoft services. In my project I have several web applications set up in IIS, and my version control structure has the trunk and an arbitrary number of branches. Whenever I would want to switch to working on a different branch of the code, I would need to reset the IIS applications to point to the new path; this quickly became more tedious than I like.</p>
<p>This seems like a good example to show what a very simple PowerShell script can do. I&#x2019;m not going to cover how to set up PowerShell, but I will say that if you are on Windows 7, it&#x2019;s already there waiting for you. For this example, you also need to make sure you have the IIS PowerShell module set up.</p>
<p>With that out of the way, let&#x2019;s get started. As I mentioned before, the directory structure of my repository is very simple, as shown in the picture to the right. And no, I don&#x2019;t really name my projects WebApplication1 and WebApplication2. The script I wanted to build was very simple: provide a menu of versions it finds, and let you pick which one to set IIS to use.<br>
If you&#x2019;re new to using PowerShell with IIS, one interesting aspect of it is the ability to browse IIS via the command line as if it were just another drive on the system. For example, let&#x2019;s take a look at the current settings before we get started:</p>
<pre><code>PS C:\Users\Greg&gt; dir &apos;IIS:\Sites\Default Web Site&apos;
Name             Application pool   Protocols    Physical Path
----             ----------------   ---------    -------------
WebApplication1  DefaultAppPool     http         C:\svn\trunk\WebApplication1
WebApplication2  DefaultAppPool     http         C:\svn\trunk\WebApplication2
</code></pre>
<p>First things first; we import the IIS library and set up the main variables used in the script:</p>
<pre><code>Import-Module WebAdministration

# set this to the root folder, which contains 2 folders: &quot;branches&quot; and &quot;trunk&quot;
$svnPath = &quot;c:\svn&quot;

# paths to the branches
$branches = Get-ChildItem ($svnPath + &quot;\branches&quot;)

# path to IIS website we are looking to configure
$iisPath = &quot;IIS:\Sites\Default Web Site\&quot;

$options = @{}
$optionCounter = 1
</code></pre>
<p>Most of that is pretty straightforward, but I&#x2019;ll comment on some of it. Get-ChildItem is a PowerShell Cmdlet to list the child items in a location, which in our case is the folder of branches. The syntax used to declare $options creates it as an empty hash table.</p>
<p>Next, we&#x2019;ll add some code to print out the options you can pick from. I make the assumption that the trunk will always be there, and then list the branches found in the previous section. There is nothing particularly interesting in this part:</p>
<pre><code># Print out trunk/branch options
write-host &quot;Select which version you want to use in IIS:&quot;
write-host &quot;&quot;

write-host &quot;1: Trunk&quot;
$options.Add($optionCounter.ToString(), ($svnPath + &quot;\trunk&quot;))

write-host &quot;---------------------&quot;
write-host &quot;Branches:&quot;
write-host &quot;&quot;

foreach ($branch in $branches) {
    $optionCounter++
    $options.Add($optionCounter.ToString(), $branch.FullName)

    write-host &quot;$($optionCounter): $($branch.Name)&quot;
}

write-host &quot;&quot;
</code></pre>
<p>As you can see, the write-host command is used to write out to the console. Similarly, read-host is used to get input from the user. We loop until you either pick a valid option in the menu, or decide to quit:</p>
<pre><code># Let the user pick an option
$input = &quot;&quot;

do {
    $input = read-host &#x2018;Choose a version (&quot;quit&quot; to cancel)&#x2019;
} while ($input -ne &quot;quit&quot; -band $options.ContainsKey($input) -eq $False)

# if they wanted to quit, we&apos;re done
if ($input -eq &quot;quit&quot;) {
    exit
}

write-host &quot;&quot;
</code></pre>
<p>Now we&#x2019;ve displayed a menu, and chosen which version of the applications we want to use in IIS. Finally, it&#x2019;s time to update the actual IIS settings. In this example, I will include a hardcoded array of projects to use, but this could just as easily be made dynamically. For now we&#x2019;ll keep it static to have total control on which applications this is applied to.</p>
<pre><code># now we can start updating IIS settings
$appPath = $options.Get_Item($input)
$projects = (&quot;WebApplication1&quot;, &quot;WebApplication2&quot;)

foreach ($project in $projects) {
    write-host &quot;Updating: $($project)&quot;

    Set-ItemProperty ($iisPath + $project) -name physicalPath -value ($appPath + &apos;\&apos; + $project)
}

write-host &quot;&quot;
write-host &quot;Done!&quot;
</code></pre>
<p>There, we use the Set-ItemProperty Cmdlet to update the physical path on each application in IIS. As I mentioned earlier, the IIS PowerShell module allows you to treat IIS applications as you would normal files in a folder, so that&#x2019;s what we leverage here. You could use the same approach to set any other aspect of the application as well.</p>
<p>Here is sample output from running the script:</p>
<pre><code>PS C:\Users\Greg&gt; powershell.exe .\Set-IIS-Paths.ps1
Select which version you want to use in IIS:

1: Trunk
---------------------
Branches:

2: 2010-01-01
3: 2010-01-15

Choose a version (&quot;quit&quot; to cancel): 2

Updating: WebApplication1
Updating: WebApplication2

Done!
</code></pre>
<p>And there you have it.  There is nothing groundbreaking here, and it only scrapes the surface of what you can do with both PowerShell and IIS, but it shows how simple it can be to write scripts to make your life easier, leaving more time to spend on doing the work you want to be doing. PowerShell also provides the ability to build rich user interfaces through WPK, available as part of the PowerShell Pack. I&#x2019;ll leave it as an exercise for the reader to convert this into a GUI.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>