June 6, 2009

Powershell Progress Bar

I needed to show a simple progress bar while I was running a long powershell script. It turns out that it is really simple to do and a snippet is found below:
#loop
for($it = 0; $it -lt $copy.length; $it++)
{
# Report progress
$percents = [System.Math]::Round(100 * $it / $copy.length)
Write-Progress -Activity "Working..." `
-PercentComplete $percents `
-CurrentOperation "$percents % complete" `
-Status "Please wait."
}
......
It displays a bunch of ooooooo as a progress bar. I really wish it had the bar style like an initial windows install, but it is not a big deal.

June 5, 2009

Powershell and online games

WoW (and I mean both wow and warcraft)! I have not played this game in years but I know many developers that still do. It seems we have a script for that as well! Here is a link to someone who wrote an updater script in POSh for World of Warcraft add-ons.

ServerFault

Seems the guys at StackOverflow finally made their "sister" public (was beta for a while). It can be found Here.

Send email using powershell

Sending an email in powershell is actually very easy using .NET. Here is how you would do it:


Set-PSDebug -Strict

# Create mail and set priority
$mail = new-object Net.Mail.MailMessage
$mail.Priority = [System.Net.Mail.MailPriority]::High

# Create from, to, and bcc
$mail.From = "test@test.com"
$mail.To.Add("test2@test2.com")
$mail.CC.Add("cc@test.com")
$mail.BCC.Add("bcc@test.com")

# Create the message
$mail.Subject = "Test Email from POSh" + " (" + [System.DateTime]::Now.ToString("yyyy-MM-dd HH:mm") + ")"
$mail.Body = "<b>Testing Application</b>"
$mail.IsBodyHtml = $true

# Set SMTP Server and create SMTP Client
$smtp = new-object Net.Mail.SmtpClient
$smtp.Host = "127.0.0.1"
$smtp.Port = "25"
$smtp.EnableSsl = $false

# Send message
try {
$smtp.Send($mail)
}
catch {
"Exception caught: {0}" -f $Error[0]
}

June 4, 2009

Ping servers using powershell

A simple check to see if your server is still alive and a poor admins script that can be run as a service:
#create a file that has IP's or ServerNames to check, one per line.
$file=gc "D:\HearbeatPing.txt"

foreach($ip in $file)
{
$OK=get-wmiobject win32_pingstatus -Filter "Address='$ip'" | Select-Object statuscode

if($OK.statuscode -eq 0) {
write-host $ip is UP}
else {
write-host $ip is DOWN -background "red"}
}

FTP in Powershell

Here is an easy way to FTP in powershell:
$src = "ftp://ftp.microsoft.com/Services/Museum/pictures/windows1box.jpg"
$dest = "c:\temp\windows1box.jpg"
$Client = New-Object System.Net.WebClient
$Client.DownloadFile($src, $dest)
To upload a file, you need to change one line:
$Client.UploadFile($src, $dest)
Oh, Windows1 was a very long time ago!

June 3, 2009

LogParser unzip and parse

All credit for this goes to Gluegood Software who saw this as a problem a few months ago and released freeware to help solve it. I was excited about this, but when I tried it I kept getting errors relating to invalid checksum. You can find Here a slightly modified version of what Gluegood did. The basic idea is the same and the files added/modified were:
  • UninstallDotNetAsCom.bat (added)
  • Module1.vb (modified)
  • Class1.vb (modified)
  • Zip.vbproj (modified)
  • Archive Folder added to Zip project
Basic idea and more details can be found on the GlueGood site (as well as some other awesome tools). In short, you would do something like this:
LogParser.exe -i:COM "select * From 'Test.tar.bz2'" -iProgId:Gluegood.LogParser.ZIP -iCOMParams:iQuery="Select * From *.txt",iInputParameters="-i:CSV -HeaderRow:Off" -o:DataGrid

LogParser.exe -i:COM "select * From 'Test.tar.gz'" -iProgId:Gluegood.LogParser.ZIP -iCOMParams:iQuery="Select * From *.txt",iInputParameters="-i:CSV -HeaderRow:Off" -o:DataGrid
This additional code now gives capability for:

.zip (always worked with original code)
.tar
.tar.bz2
.tar.gz

One last note is that the original code that gluegood wrote may work for you and I just may have been the unlucky one who had some odd gzipped files...

AzMan Test Harness

I (or someone from our operations team) often needed a way to quickly test operations in my applications. This had to be done without going directly into our main application or digging into the AzMan console. I whipped up (back in '04 but still used today) this simple windows form. I have uploaded my old test harness here . This harness is very old (written in .NET 1.1), so you may have to update some code if you are putting this into 2.0 or 3.5. The windows form when run will allow you to test:
  • Test either against XML or AD store
  • Specify the Application Name
  • Specify the OperationName
  • Test with either a User SID or a UserName
The output would then show if that SID or UserName is True/False against that operation.

Powershell Exception Helper

Powershell does not really have a good way to log and handle exceptions. You can find a bunch of articles on the Trap keyword and what can be done with it. Here is one great article on the topic. Having said that, my co-worker 'AC' did it again and created this wonderful helper file which I figured I would share with you. You can use this code inside every function at the first line. This works very well if you have many posh scripts being called:
function MyFunction($param1, $param2)
{
trap { TrackError }
....
}

$global:trackedErrorHashCode = $null

function TrackError
{
$err = $error[0]
$hashCode = $err.GetHashCode()

if($global:trackedErrorHashCode -eq $hashCode) {
return
}
else {
$global:trackedErrorHashCode = $hashCode
}

$text = BuildErrorMessage $err
WriteErrorMessage $text
SaveErrorMessage $text

throw $err
}

function BuildErrorMessage($err)
{
$text = ""
$text += FormatAsLine "Error ----------------------"
$text += FormatAsLine
$text += FormatAsLine "$($err.InvocationInfo.InvocationName): $($err)"
$text += FormatAsLine $err.InvocationInfo.PositionMessage
$text += FormatAsLine
$text += FormatAsLine "$($err.Exception.GetType().Name)"
$text += FormatAsLine

$text += FormatAsLine "StackTrace -----------------"
$text += FormatAsLine
$info = $err.InvocationInfo
$stack = Get-PSCallStack

$text += FormatCallStackLine $stack[2].command $info.ScriptName $info.ScriptLineNumber

$stack[3..$stack.length] | % {
$text += FormatCallStackLine $_.Command $_.ScriptName $_.ScriptLineNumber
}
$text += FormatAsLine "----------------------------"

$text
}

function FormatAsLine($text)
{
$text + "`n"
}

function FormatCallStackLine($command, $file, $lineNumber)
{
" At '$command' in $($file):$lineNumber`n"
}

function WriteErrorMessage($text)
{
Write-Host $text -fore green
}

$global:_savedErrorMessages = @()

function SaveErrorMessage($text)
{
$global:_savedErrorMessages += $text
}

function GetErrorMessages()
{
$global:_savedErrorMessages
}

June 2, 2009

Generate enum for AzMan Operations

I wrote this tool a long time ago and wanted to just point it out in my blog. It will generate for you an enum of AzMan operations that you can now call in your code. This was very handy for me in my applications.

Logparser SQL Server 2005 Log

Logparser is one of my favorite tools. Here is a simple query that will parse a sql server 2005 log:


SELECT TOP 25 extract_token(field1,0,' ') as Date, extract_token(field1,1,' ') as Time, extract_token(field1,2,' ') as Source, field2 as Message
FROM c:\sql.log
ORDER BY Date, Time DESC

AzMan Common Errors

Here is a list of common errors that I had while working with AzMan. I'll keep this updated if I come across any new ones (especially now with the SQL store as the new backend).
  • Unable to update the password. The values provided as the current password is incorrect.

  • This is an issue with the user making a call out to AzMan. You should set Everyone as a Reader or turn impersonation on (if cross-domain). You should see full details in the event log where AzMan is located.

  • COM error 80040154 or Retrieving the COM class factory for component with CLSID {...}

  • The error is that .NET can't find the AzMan COM objects. If you copy azroles.dll into %WINDIR%\System32\and then run regsvr32 azroles.dll you should be ok. You can also GAC the Microsoft.Interop.Azroles.dll which is found in %WINDIR%\Microsoft.NET\AuthMan .

  • File not found (80070002)

  • The solution for me was to Add the Authenticated Users to the Reader mode in AzMan. You can also try and add everyone (not the best idea) to the reader mode.

  • Element not found (80070490)

  • You usually get this when the msldap or msxml string is wrong. Check your connection string and make sure it can talk to the right domain or XML file. Also, I have seen this if you attempt to talk to an operationName or operationID that is not there or spelled incorrectly.

  • This operation can not be performed on the current domain.

  • Seems to be tied to mismatched SIDs or going cross domain. Usually, this is just a trust issue. You can also try and impersonate to solve this problem. This error is tied to an AD backed store and you should not really see this in an XML store system.

  • The specified domain either does not exist or could not be contacted

  • A solution for this can be found Here. Personally, I never had this error happen to me.

  • The system cannot open the device or file specified 98007006

  • Another security/trust issue. Make sure you have correct roles to read AzMan and the trusts or impersonation is set in place correctly.

  • Unable to cast COM object of type 'System.__ComObject' to interface type 'Microsoft.Interop.Security.AzRoles.IAzApplication'

  • Solution can be found Here.

  • The authorization store component is not installed

  • Make sure you GAC the AzRoles Interop Assembly.

  • FileLoadException: The process cannot access the file because it is being used by another process

  • Something with how the DOM opens up XML. I never really had this issue as I oly worked with AD store.

AzMan on XP

I know that most people know this by now, but it was one of the major issues that I ran into. In order to get this working on XP you must download the adminpak.exe located Here.

AzMan (history lesson)

Before we actually dive into code and how to use AzMan within our applications, it is important to know what AzMan is.

AzMan was rolled into Windows Server 2003 a few years ago and did not really get that much attention. When I was building an application a few years ago, I needed a fine grained approach to authorizing users. I wanted a real RBAC system and something that I would be able to use within a .NET application. I read about this COM based API called AzMan that would be able to solve my problems. It is a full RBAC system and had a backing store (at the time) of AD or XML. The breakdown was basically that a user had a Role(s) and a Role was made up of other Role(s), Task(s), or operation(s). Tasks were made up of operation(s) or other Task(s) and operations were low level "methods". Another way would be to say that a role was Executive and Executive had a task of Exec and ExecTask had an operation of DoEverything.

Most people don't remember that Microsoft actually released some open source code to help with AzMan. Back in the day before EntLib, we had Microsoft Application Blocks. One of the blocks was Microsoft.ApplicationBlocks.Security. This had some form of a wrapper around the Interop, but it was very confusing. If you look back or serach Google you may be able to find the original AzManProvider.cs that Microsoft released. At that time, I wrote a wrapper around the wrapper that Microsoft wrote! Here is what it looked like:


using System;
using System.Threading;
using System.Collections.Specialized;
using System.Security;
using System.Security.Principal;

using Microsoft.ApplicationBlocks.Security.Authorization;

namespace Microsoft.ApplicationBlocks.Security.Wrappers
{
///
/// Summary description for AuthorizationHelper.
///

public sealed class AuthorizationHelper
{
private ExtendedPrincipal principal;

public AuthorizationHelper(IIdentity ident, string auditID, String provName, string appName)
{
principal = ExtendedPrincipal.Create(ident);
Thread.CurrentPrincipal = principal;
principal.AuditIdentifier = auditID;
principal.AuthorizationProvider = provName;
principal.AuthorizationParameters.Add(AzManProvider.ApplicationName, appName);
}

public AuthorizationHelper(IIdentity ident, string auditID, String provName)
{
principal = ExtendedPrincipal.Create(ident);
Thread.CurrentPrincipal = principal;
principal.AuditIdentifier = auditID;
}

public bool CheckAccess(string scope, string operation)
{
principal.AuthorizationParameters["scope"] = scope;
principal.AuthorizationParameters["operation"] = operation;

return principal.CheckAccess();
}

public StringCollection GetRoles()
{
return principal.GetRoles();
}

public bool IsInRole(string role)
{
return principal.IsInRole(role);
}
}
}


That was some very old .NET 1.1 code and a wrapper around what Microsoft wrote. It allowed me to then do the following in my application:


AuthorizationHelper auth = new AuthorizationHelper(WindowsIdentity.GetCurrent(), "auditID", "azmanProvider", "TestApplication");

lstRoles.DataSource = auth.GetRoles();
lstRoles.DataBind();


Not the best, but it allowed me to at least get what I needed. From that point though, I ran into many issues with AzMan and also as newer versions of .NET came out my code changed dramatically. I'll share this as I cotinute writing posts on AzMan.

June 1, 2009

Bing!

I read about this last week on some site. It looks like Microsoft is making another solid attempt at a search engine. Take a look at Bing and let me know what you think!

Get Modified users in AD using Powershell and C#

Using c# within powershell you can easily get users that have changed in AD. Just change the date found in the whenChanged part of the query (20090601):
$strFilter = "(&(objectClass=User)(whenChanged>=20090601000000.0Z)(userAccountControl:1.2.840.113556.1.4.803:=2))"

$objDomain = New-Object System.DirectoryServices.DirectoryEntry

$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter

$colProplist = "name"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}

$colResults = $objSearcher.FindAll()

foreach ($objResult in $colResults)
{$objItem = $objResult.Properties; $objItem.name}
On a side note, you can also do this very easily with the free Quest AD cmdlets

IronPython

I avoided it for a while, but it seems I may soon have to dive into it. Time to start learning it: Hello World in IronPython!

Remote Execution of Powershell

Powershell 2 has some really cool features. One of the features I really like is the remoting portion. Although does not work on Vista, for me I just needed it to work on Win2k3. You will also need powershell installed on that server you are trying to connect to.

Here is some sample code and a helper script (thanks to my co-worker 'AC' for this one) to get it to work:
function ExecuteScriptRemotely($script, $scriptArgs, $server, $cred, $logFile)
{
$cmd = " & { $script } $scriptArgs "

if($logFile)
{
$cmd += " > `"{0}`"" -f $logFile.Replace('"', '')
}

$encodedCommand = EncodeCommand $cmd
$commandLine = "powershell -ExecutionPolicy Bypass -EncodedCommand $encodedCommand"
StartRemoteProcess $commandLine $server $cred
}

function StartRemoteProcess($commandLine, $server, $cred)
{
if($cred)
{
Invoke-WMIMethod -path win32_process -name create -argumentList $commandLine -credential $cred -computerName $server
}
else
{
Invoke-WMIMethod -path win32_process -name create -argumentList $commandLine -computerName $server
}
}

function EncodeCommand($expression)
{
#Convert the command into an encoded command for PowerShell
$commandBytes = [System.Text.Encoding]::Unicode.GetBytes($expression)
$encodedCommand = [Convert]::ToBase64String($commandBytes)
$encodedCommand
}

function CreateCredentials($user, $password)
{
$password = convertto-securestring $password -asplaintext -force
New-Object System.Management.Automation.PSCredential $user, $password
}
Here is how you would use it:
$MY_DIR = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
. (Join-Path $MY_DIR RemHelper.ps1)

# Create c:\bar.txt
$script = '"foo" > "c:\bar.txt"'

# run notepad
$script = 'notepad'

$scriptArgs = $null
$server = "MyServer"
$user = "Admin"
$password = "Password"

$cred = CreateCredentials $user $password
ExecuteScriptRemotely $script $scriptArgs $server $cred "c:\log.txt"

Messed up my Portable HD

Ughh...I messed up my Passport HD. I converted it to NTFS and I forgot that my DVD player reads Fat32. I'd call this a brain-freeze for the day! I needed to convert back to Fat32 and wanted a fast utility. I found Fat32Format to be the fastest option.

...have to backup my music before I format though!

Transcript Log in Powershell

How many times have you typed up a script and then by accident closed the command prompt. I can tell you it has happened to me a few times! Sure, I can probably whip it up again in a few minutes, but it is annoying! Well, here is something that I came across:
Start-Transcript D:\tmp\Transcript.txt

# Do some work
get-wmiobject win32_computersystem

Stop-Transcript
This will create a log called Transcript.txt that contains everything you did with powershell for that session. If you like, you can have it start up each time by setting this in your environment on powershell startup.

There are a few things to note with this:
  • This will write over any previous sessions unless you set it up to append by using -Append $true in your Start-Transcript
  • This works only in the command line and not in the ISE. I guess this was missed?
  • This will log everything you type within the session

May 31, 2009

Free e-book on Powershell

I know a few exist online, but I really like the Effective Windows Powershell written by Keith Hill. You can download it HERE

Replace macro fields with Ruby

OK, now let's do it in Ruby. This requires a values.rb folder (like our values.ps1 folder in powershell):


require 'erb'
require 'rio'

module MacroConfig

def self.generate
mkdir_if_not_exist 'configs'

Dir.glob('templates/*').each do |template_file|
template = ''
rio(template_file) > template
config = ERB.new(template).result(get_values)

config_file = 'configs/' + File.basename(template_file)
rio(config_file) < script =" ''">> script
script << "\n"
end

eval script
binding
end

def self.get_values
read_values(['../values.rb', 'values.rb'])
end

def self.mkdir_if_not_exist(dir_name)
Dir.mkdir dir_name unless File.exist? dir_name
end

end