Thursday, April 28, 2011

Receiving (401) Unauthorized when accessing SharePoint server using webclient

I was trying to build a quick script to check on the status of a bunch of SharePoint servers. This is the code I was using:

$webclient = New-Object System.Net.WebClient
$webclient.Headers.Add("user-agent", "Powershell webclient")
$webclient.Credentials = New-Object System.Net.NetworkCredential("usernam", "password", "domain")

However, this is the error I got:

Exception calling "DownloadString" with "1" argument(s): "The remote server returned an error: (401) Unauthorized."
At line:1 char:26
+ $webclient.DownloadString <<<< ("")
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

I searched high and low but found nothing useful. Then out of the blue I thought I would try


and lo and behold, the page loaded. I guess there must be something going on where webclient does not handle the default document or something to do with how I setup my SharePoint site. I don't have time to dig deeper into the root cause, but problem solved, moving on.

Wednesday, April 20, 2011

Resizing Amazon EC2 Windows drives

The disk sizes that come with the Amazon Windows AMIs are a little small (35GB for a Windows 2008 R2). Since I am running demo servers I have not really been paying too much attention to laying out the disks properly. However, the 35GB disk is simply not enough so I had to resize it. This turned out to be surprisingly easy:
  1. Stop the instance
  2. Create a snapshot of the volume
  3. Create a new volume from that snapshot, specifying the new size
  4. Detach old volume from the instance
  5. Attach new volume to the instance
  6. Restart the instance
  7. Run diskpart and enter the following commands
    • select disk <disk number>
    • select volume <volume number>
    • extend
  8. Delete the old volume (unless you want to keep it as a backup)

Sunday, April 17, 2011

Amazon EC2 Powershell environment setup

I always keep forgetting these so I figured I put them here:

$env:JAVA_HOME = 'C:\Program Files\Java\jre6'
$env:EC2_HOME = 'C:\Program Files\Amazon\ec2-api-tools'
$env:EC2_PRIVATE_KEY = '<path to private key>'
$env:EC2_CERT = '<path to cert>'
$env:Path = $env:Path + ";C:\Program Files\Amazon\ec2-api-tools\bin"

Resizing an Amazon EC2 instance

We had overbuilt some of our Amazon instances because we were not sure of the performance requirements. This was costing us lots of money so we recently started downsizing some of these instances. This is how I was doing it:

  1. Confirm that Ec2SetComputerName is disabled
  2. Stop the instance
  3. Launch a new instance in the same zone
  4. Stop the new instance
  5. Attach the volumes from the old instance to the new one
  6. Start the new instance
  7. Terminate the old instance
  8. Delete the volume that came with the new instance
  9. Reassociate the Elastic IP address

Wow, that was rather cumbersome, but that is the only way to do this via the EC2 console. Looking at the API, I found the following Powershell command that replaces steps 3 to 8:
ec2-modify-instance-attribute <instance-id> --instance-type <new-instance-type>

That's it! I can't believe I did it the hard way that many times.

Thursday, April 14, 2011

Changing the master page

I needed to change a master page today. It looks like if you want to do this through the web UI you first need to enable SharePoint Server Publishing Infrastructure site collection feature and the SharePoint Server Publishing site feature. Then there will be a link to Master Page Settings under Site Settings->Look and Feel. On top of that I found lots of complaints about the Top Link Bar disappearing after deactivating the publishing features. Sounds like a pain in the ass.

Well, instead of doing that through the Web UI, here's how you would do that through Powershell:
$web = Get-SPWeb "<site url>"
$web.MasterUrl = "<relative url to your master page>"

Done! That's it. No screwing around with temporarily enabling features. Now, why doesn't the UI make it as easy? Who knows.

Wednesday, April 13, 2011

Parsing IIS logs with Powershell (because logparser not supported on Windows 2008)

I was very disappointed to find out that logparser is not supported on Windows 2008. So, I started to look for an alternative and found a TechNet post by Nick Goude on how to use Powershell to parse IIS logs.

I have, for the most part, simply lifted the code:

# Location of IIS LogFile
$File = "C:\inetpub\logs\LogFiles\W3SVC25824252\u_ex1104*.log"

# Get-Content gets the file, pipe to Where-Object and skip the first 3 lines.
$Log = Get-Content $File | where {$_ -notLike "#[D,S-V]*" }

# Replace unwanted text in the line containing the columns.
$Columns = (($Log[0].TrimEnd()) -replace "#Fields: ", "" -replace "-","" -replace "\(","" -replace "\)","").Split(" ")

# Count available Columns, used later
$Count = $Columns.Length

# Strip out the other rows that contain the header (happens on iisreset)
$Rows = $Log | where {$_ -notLike "#Fields"}

# Create an instance of a System.Data.DataTable
#Set-Variable -Name IISLog -Scope Global
$IISLog = New-Object System.Data.DataTable "IISLog"

# Loop through each Column, create a new column through Data.DataColumn and add it to the DataTable
foreach ($Column in $Columns) {
  $NewColumn = New-Object System.Data.DataColumn $Column, ([string])

# Loop Through each Row and add the Rows.
foreach ($Row in $Rows) {
  $Row = $Row.Split(" ")
  $AddRow = $IISLog.newrow()
  for($i=0;$i -lt $Count; $i++) {
    $ColumnName = $Columns[$i]
    $AddRow.$ColumnName = $Row[$i]


Now, if you save this to a file such as iislog.ps1, then you can run commands like:

.\iislog.ps1 | Select-Object csusername | Sort-Object -Property csusername | Get-Unique -AsString

Note, there are some glaring deficiencies:

  1. Parameterize the specification of log files
  2. Handle column name changes
  3. Handle extra headers (these are saved upon iisreset) - done
  4. Stream results back out so that they can be used in a pipeline

I hope to fix these soon, but need to get to sleep.

Monday, April 11, 2011

SPNavigationNode object won't take title

I was trying to script the creation of Quick Launch links. This appears to be quite straightforward. Here is the Powershell:
$SPSite = Get-SPSite "<some URL>"
$OpenWeb = $SPSite.OpenWeb()
$QuickLaunch = $OpenWeb.Navigation.QuickLaunch

$node = New-Object Microsoft.SharePoint.Navigation.SPNavigationNode("Some Title", "<some URL", $true)

At this point I was curious to see what was in the $node object. So, I took a look and it looks something like this:

Title                  :
TitleResource          :
IsVisible              :
IsExternal             : True
Id                     : 0
ParentId               : 0
Parent                 :
Navigation             :
Url                    : <some URL>
LastModified           : 1/1/0001 12:00:00 AM
Children               : {}
Properties             :
TargetSecurityScopeId  : 00000000-0000-0000-0000-000000000000
TargetParentObjectType : Web

So, why is the Title blank? I tried a whole bunch of things with no luck. Then I read the documentation of SPNavigationNode more carefully. In the Remarks section, it states: "The new SPNavigationNode object is not completely initialized until it has been added to a collection. For more information, see the SPNavigationNodeCollection class."

Are you kidding me? Creating an object through a constructor does not fully create the object. You have to add it to some kind of collection and that has the side effect of filling in the rest of the properties. IMHO, that is very poor design.

Oh well, so I continued with adding the node with the following Powershell and all is good: