Adding another admin user to Windows Azure Pack (WAP)

I’ve just finished setting up a lab environment with DSC and SMA and a colleague of mine also wanted a go in my new SMA playground. I created a user account for my colleague in my domain and then switched over to my Windows Azure Pack (WAP) server used to administer the SMA server. Surprisingly it was not immediately obvious how to give him access to the admin site. I was expecting a simple button to provide access to an account.

Which brought me to my first question:

Which accounts have admin access?

This wasn’t immediately obvious either.

After some investigation I found that I could see the admin users in [Microsoft.MgmtSvc.Store].[mp].[AuthorizedAdminUsers] table in the SQL database on the WAP server.
Some more research led me to the PowerShell module MgmtSvcConfig where you will find a cmdlet named Get-MgmtSvcAdminUser. Guess what it does?

So to see which accounts have admin access:

Get-MgmtSvcAdminUser

Then back to our original question

How do I add another admin user to Windows Azure Pack?

By using the same PowerShell module as before. This time using the Add-MgmtSvcAdminUser cmdlet.

Like this:

Add-MgmtSvcAdminUser -Principal "Domain\user"

 

Is there a way to add an admin user via GUI? Perhaps. I didn’t find a way. To be fair though I didn’t spend a long time looking when I found the PowerShell cmdlets.

How to determine which PowerShell version you are running

Ever needed to find out which PowerShell version you’re running?

Don’t be fooled by the path of the PowerShell executable which indicates version 1.0.
C:\WINDOWS\system32\WindowsPowerShell\v1.0

Fire up PowerShell and enter

$PSVersionTable

PowerShell Christmas Spirit

Hey everyone! Merry Christmas!

This Christmas I won’t be doing much coding but that doesn’t mean no PowerShell now does it?
To get my daily intake of PowerShell I’ve made some PowerShell Ginger Biscuits.

Luckily for me I’m far better at PowerShell than I am at decorating Ginger Biscuits…
But I had some fun and here they are!

Gingerbread_PowerShell

Merry Christmas!

Using PowerShell to delete IIS log files

It’s that time of the month again.

No, not that time. I’m talking about the «Damn! IIS log files are filling up the server again». That time of the month. All sysadmins have been there at some time or another. You’re responsible for some webserver with a fair amount of traffic, and you regularly need to delete the old log files to free up disk space. Finally you decide to try and automate the process, but how to do it? You could use any number of methods to get the job done, I am of course going to focus on using PowerShell.

Note that the following will not work with older versions of IIS than 7.5 (that means earlier than Server 2008 R2)

Where are my log files?

Finding these log files is the first step of our script. The location of IIS log files may vary from site to site. By default they are all placed in several folders in «%SystemDrive%\inetpub\logs\LogFiles».

I was keen to solve this problem for multiple servers and would rather not have to specify the directory of the log files, only the name of the website. The PowerShell code requires a bit of tasty regex to ensure the flexibility we’re after.

Import-Module WebAdministration

$Website = Get-Item IIS:\Sites\$WebsiteName
 $LogFileDirectory = $Website.logfile.directory

if ($LogFileDirectory -match "(%.*%)\\") {
 $LogFileDirectory = $LogFileDirectory -replace "%(.*%)\\","$(cmd /C echo $matches[0])"
 }

Ok, ok! So regex can be a little confusing, what the heck is going on here?

The log file directory is often specified using environment variables such as %SystemDrive%. PowerShell doesn’t understand environment varibles in the old cmd format and therefore we have to translate them using cmd.

In order to get this done automatically without user interaction, the regex will check to see if the log path starts with an environment variable and if so, run echo in cmd and pass the output back to PowerShell. Thereby ensuring paths beginning with %SystemDrive% or %windir% or whatever will not cause our script to fail.

Delete, Delete, Delete!

With our log folder located, it’s time to get our hands dirty. What’s next? Right. Delete those damn log files!

The obvious issue here is making sure we actually only delete the files we want to. We really don’t want to be in the situation of explaining that the reason the website is returning 404 errors is because we deleted the inetpub folder. The code to get the job done is a piece of cake:


$toBeDeleted = Get-ChildItem -Path $LogFileDirectory -Recurse | where {
 $_.LastWriteTime -lt (Get-Date).adddays(-$DaysToRetain) -and $_.PSIsContainer -eq $false
 }

if ($toBeDeleted -ne $null) {
 $ToBeDeleted | % {
 Remove-Item $_.FullName
 }
 }

I don’t feel the need to explain this code in detail – List all files, delete all files. Simples.

Script Parameters

I’ve only added two parameters for this script.

Let’s start off by defining which website we would like to target. Keep in mind that websites often (and by default) share the same log files folder and therefore you may be deleting logs for several sites even though you specify a website. If this is an issue you will need to define separate log file locations for each site before running this script.

How many days of retention do we want? I’m going to default this to 30 days but add the parameter for flexibility. Because of the default value, I’m not even bothering setting this to mandatory.

Putting it all together in code

TL;DR? Here is all the code sewn together in perfect harmony. Perfect harmony does of course mean that you should run this on a test environment before deploying it but you knew that already didn’t you?

Function Clean-IISLogs {
 Param(
 [Parameter(Mandatory=$true)]
 [String]$WebsiteName,

[Parameter(Mandatory=$false)]
 [Int]$DaysToRetain = 30
 )

Import-Module WebAdministration

$Website = Get-Item IIS:\Sites\$WebsiteName
 $LogFileDirectory = $Website.logfile.directory

if ($LogFileDirectory -match "(%.*%)\\") {
 $LogFileDirectory = $LogFileDirectory -replace "%(.*%)\\","$(cmd /C echo $matches[0])"
 }

$toBeDeleted = Get-ChildItem -Path $LogFileDirectory -Recurse | where {
 $_.LastWriteTime -lt (Get-Date).adddays(-$DaysToRetain) -and $_.PSIsContainer -eq $false
 }


if ($toBeDeleted -ne $null) {
 $ToBeDeleted | % {
 Remove-Item $_.FullName
 }
 }

}

And there you have it. Now the only thing between you and an IIS server that doesn’t require you to clean old log files is your task scheduler.

PowerShell Basics: Getting help

When starting out in PowerShell, you’ll need to spend a lot of time figuring out how to use various cmdlets. Google can help you, but PowerShell already provides a cmdlet for this purpose. The cmdlet is called Get-Help and is very useful.

Usage:

Note that if you’re using PowerShell 3.0, you will have to run Update-Help before any help is available, this command requires an internet connection. It must also be run from an elevated PowerShell session.

The usage of the command is: Get-Help <cmdlet>
Like this:

#Use any cmdlet you want after Get-Help
#Example:
Get-Help Get-Process

Running this will give you a lot of useful information about how to use the cmdlet, including which parameters are accepted and aliases you can use for the cmdlet.

There are several switches you can add to Get-Help to get the information you want. Try the following commands to get a feel for the different options.

Get-Help Get-Process -Full
Get-Help Get-Process -Detailed
Get-Help Get-Process -Examples

If nothing else, remember this:

Perhaps the most useful usage of Get-Help is using it with the -Examples switch. This one will show you command examples to illustrate the usage.

Capture

Tips and tricks: PowerShell ISE 3.0

I haven’t written anything for a while and much has happened in the world of PowerShell since my previous post.
To sum it up: Windows 8 and Server 2012 have been released and with them – PowerShell 3.0.

What’s new in PowerShell 3.0?
Lots. And then some.
I’m not going to go through all the new stuff now, but here is a good summary: http://technet.microsoft.com/en-us/library/hh857339.aspx

I am instead going to post a few tricks you can do with the new edition of the PowerShell Integrated Scripting Environment (ISE).

1. Edit tab titles

I often work with multiple tabs in the ISE and when all new tabs automatically get named “PowerShell” followed by a number, it doesn’t take many tabs before losing control of which tabs are which.

What surprises me is that there is no way to right-click the tab and select Rename. I can’t help but think that would be a trivial thing to add.

This is how to rename the titles of your tabs:

$psISE.CurrentPowerShellTab.DisplayName = &quot;Custom name&quot;

2. Insert Snippets

While perhaps mostly a beginners function, the 3.0 version of the ISE allows insertion of some template “snippets”.

These are syntactically correct code blocks with templates for if statements, while loops, for loops and so on.

My personal favorite is the Cmdlet (advanced function). I always disliked writing the documentation part at the beginning of my Cmdlets and this one does some of it for me.

Snippets can be found on the edit menu or by using the Ctrl+J shortcut.

3. ISE Add-ons

The ISE has built-in support for add-ons. You can find add-ons by going to the Add-ons menu in ISE (Add-ons -> Open Add-on Tools Website)

The website has add-ons ready for download. It also provides some guides for those wanting to write their own ISE add-ons.

PowerShell Basics: Primitive data types

One of the best things about PowerShell is how easy it is to use. It is a good idea to know a little bit about whats going on under the hood though. Although you can manage fine doing most things in PowerShell without thinking about data types, knowing about them will really help you understand what is going on and hopefully help you spend less time debugging your scripts.

 

What is a datatype?

Straight off Wikipedia: data type is a classification identifying one of various types of data.
Now that’s not really very helpful, is it?

Think of it like this:

If you have a number, lets say 2 – PowerShell would by default store that as a 32-bit integer – which is basically a fancy word for a number without decimals. Integer values cannot have decimals.

Lets try a different number, 2,4 – PowerShell would by default store that as a 64-bit value with a data type called a “Double”. Double values are for numbers with decimals, also known as precision numbers.

One of the most used data types and most significant is the “String”. A String is a collection of characters. When defining a string value, we always enclose the value in double quotes. like this:

 $String = "Hello" 

Open your shell and try the command without double quotes. Spoiler: You’ll get a big red error message.

A detailed list of data types for .NET can be found here: http://msdn.microsoft.com/en-us/library/47zceaw7(v=vs.71).aspx

Short demonstration if you haven’t already done so yourself:

Note the method gettype(). You can use that on any object to figure out the data type, it is very useful during debugging.

 

Usage in PowerShell

As we have already seen, PowerShell chooses the appropriate data type for us, allowing us to think about other things. But if we really want to have full control we can specify it ourselves like this:

[Int32]$number = 2
[Double]$number2 = 2.4
[String]$string = "hello"

Remember how Integers cannot have decimals? Try this and see what you get:

[Int32]$number = 2.4
Write-Host $number

Because Integers cannot have decimals, it rounds the number to the nearest non-decimal number.

Again I would like to stress the usefulness of the method gettype(). Get into the habit of using it and you won’t be wondering what data type your script is returning.

 

Enough boring theory! Why is this useful?

Consider the following code:

$number1 = "2"
$number2 = "2"
$number1 + $number2

Without knowledge of data types you might think the result would be 4.
However, because we enclosed the values in double quotes, the variables have become Strings instead of Integers. Add the two Strings and you get “22”.

Now consider this code:

[Int32]$number1 = "2"
[Int32]$number2 = "2"
$number1 + $number2

This time we have told Powershell to use Integers as our data types and get the expected result 4.
Note that what we are doing here is actually defining $number1 and $number2 as Strings and then converting them to Integers. This is a technique known as ” typecasting”.
If we wanted Integers we should not have used double quotes in the first place.

As we can see in the above examples, getting the type wrong can result in unexpected results.

Perhaps the most useful place to specify data type, is when defining parameters for a function. Validating input is very important and defining which data type you use will be very useful in the validation process. Think about it – When you ask for a user to enter his or her age, you expect something like 25 or 40. You don’t want your script to continue if the user types “Banana” or 42,6.