Life of a Geek Admin

The Daily adventures of a true geek administrator

Life of a Geek Admin - The Daily adventures of a true geek administrator

Displaying Logical Drives with PowerShell

PowerShell is a powerful scripting language and can do many things. One task that can be used over and over is to get the logical drives off a system. One you have this information you can then perform tasks like searching for files, directories, reporting usage etc..

In this post I will be simply showing how to create a variable and retrieve the data for the logical drives using WMI object win32_logicaldisk.

If we just enter Get-WmiObject win32_logicaldisk we see the output showing Device ID, DriveType, ProviderName, FreeSpace, Size and VolumeName.

PS C:\Windows\system32> Get-WmiObject win32_logicaldisk
DeviceID     : B:
DriveType    : 3
ProviderName :
FreeSpace    :
Size         :
VolumeName   :

DeviceID     : C:
DriveType    : 3
ProviderName :
FreeSpace    : 59039604736
Size         : 159724335104
VolumeName   : Windows 7

DeviceID     : D:
DriveType    : 5
ProviderName :
FreeSpace    :
Size         :
VolumeName   :

DeviceID     : E:
DriveType    : 5
ProviderName :
FreeSpace    :
Size         :
VolumeName   :

Now let’s take this a bit further and tell it we want DriveType 3. Which in this instance we see drive D: removed from the list, which is DriveType 5 (CD-ROM)

PS C:\Windows\system32> Get-WmiObject win32_logicaldisk| ?{$_.drivetype -eq 3}

DeviceID     : B:
DriveType    : 3
ProviderName :
FreeSpace    :
Size         :
VolumeName   :

DeviceID     : C:
DriveType    : 3
ProviderName :
FreeSpace    : 59039604736
Size         : 159724335104
VolumeName   : Windows 7

So what can we do next? Well, how about let’s create a list of just the drive name? No problem, let’s build on the command and add the foreach-object and grab the name.

PS C:\Windows\system32> Get-WmiObject win32_logicaldisk| ?{$_.drivetype -eq 3} | foreach-object {$_.name}
B:
C:

Now we have something we can work with and can assign this to a variable that we can use in a script. Let’s take the line and add $drives = to the front of the command and create the $drives variable which we can reference.

$drives = Get-WmiObject win32_logicaldisk| ?{$_.drivetype -eq 3} | foreach-object {$_.name}

Now lets go a step further and get the first level of the directory for the drive. We need to do a foreach and use the Get-ChildItem cmdlet.

$drives = Get-WmiObject win32_logicaldisk| ?{$_.drivetype -eq 3} | foreach-object {$_.name}
foreach ($drivename in $drives)
{
cd -Path $drivename
Get-ChildItem $drivename
}

So as you can see there are many other possibilities from what was touched on here. Have fun and keep PowerShelling !!!

How to Backup System State with PowerShell

As tweaker’s there is always changes needed to the system to make it run to our liking and there is always the risk that those changes will break and possible have major repercussions. That’s where restore points come into play. With PowerShell there is a way to use system restore points to backup and restore if an issue does arise.

This process assumes that system restore points are enabled and you have administrative privileges on your system. Restore points is only available with Windows XP and higher (Vista, 7) and not on servers.

First we need to create the restore point. Open a PowerShell session on your system and type the following.

PS C:\> Checkpoint-Computer -Description 'Before script execution'

The Get-ComputerRestorePoint cmdlet returns all the restore points available on your system. You could filter out your new restore point with Where-Object and search for the object.Description equals.

PS C:\> Get-ComputerRestorePoint


PS C:\> Get-ComputerRestorePoint | Where-Object { $_.Description -eq 'Before script execution' }

Now that we have our restore point we can revert to it Using the SequenceNumber using the Restore-Computer cmdlet. The sequence number can be retrieved by using the output of the Get-ComputerRestorePointwe ran earlier.

PS C:\> Restore-Computer 60 -WhatIf

We can also use a variable and combine the Get-ComputerRestorePoint with filter.

PS C:\> $seq = Get-ComputerRestorePoint | Where-Object { $_.Description -eq 'Before script execution' } | Select-Object -ExpandProperty SequenceNumber

PS C:\> Restore-Computer $id -WhatIf

Not so bad and all done at the power of the command line with PowerShell.

While there are no command line tools for deleting restore points, you can disable them using Windows PowerShell in an elevated session.

PS C:\> Disable-ComputerRestore "C:\"

When you need to re-enable it run:

PS C:\> Enable-ComputerRestore "C:\"

Microsoft Script Explorer for Windows PowerShell

While digging around on the Internet for some PowerShell code I came across a new tool from Microsoft called Microsoft Script Explorer for Windows PowerShell (pre-release).

Microsoft Script Explorer for Windows PowerShell (pre-release) helps scripters find Windows PowerShell scripts, snippets, modules, and how-to guidance in online repositories such as the TechNet Script Center Repository, PoshCode, local or network file systems and Bing Search Repository. It was released on 3/12/2012 and looks to be a pretty handy tool to add to your PowerShell arsenal!!

Creating Folder Shares with PowerShell

Here is a cool little function that you can use to create a share on a system. Save this function in a PowerShell module and load to your toolset.To run this cfunction you must have full administrator privileges, this function also sets the ErrorActionPreference to stop.

function New-Share {
param($Path, $Name)
try {
$ErrorActionPreference = 'Stop'
if ( (Test-Path $Path) -eq $false) {
$null = New-Item -Path $Path -ItemType Directory
}
net share $Name=$Path
}
catch {
Write-Warning "Create a new share: Failed, $_"
}
}

To use the new function open up a PowerShell command and type:

PS C:\> New-Share c:\temp TempShare

TempShare was shared successfully.

Now you should be able to see the new share you have created. You will want to go in and set access permissions to the new share you created.

Adding Additional Fonts to PowerShell and Command Console

Ever wanted to add a different font to PowerShell and command prompt, we’ll here is an easy way to add it with PowerShell.

Open a PowerShell with Administrator privileges and execute the following commands.

$key = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Console\TrueTypeFont'
Set-ItemProperty -Path $key -Name '000' -Value 'Courier New'

To select a font, right-click the icon in the console title bar and choose “Properties”, then click on the “Font” tab. Select the font and the size you want and click “Ok”.

If you want to add others just make sure you have the name ex: Calibri and just add another 0 (zero) to the -Name parameter.

Converting User Names to SIDs with Powershell

While doing some Active Directory work ran across a need to translate a user name to a SID (security identifier)  to help find traces of it in the registry. Here is an easy way to do this with PowerShell function.

function getSID($name, $domain=$env:userdomain) {
$objUser = New-Object System.Security.Principal.NTAccount($domain,$name)
$strSID =
$objUser.Translate([System.Security.Principal.SecurityIdentifier])
$strSID.Value
}

Try it with your login and see what you get.

PS:> getSID <yourid>

Extending PowerShell with modules

With Microsoft adding PowerShell to Windows it opened many different possibilities for the scripter’s and IT Administrators managing hundreds and thousands of servers with automation from their desks. One of the problems with running PowerShell from the desk is that you need modules so you can write such great scripts. In this post I will list a few of the great resources to find the modules and in future posts plan to show scripts based on some of the modules.

Installing the Remote Server Administration Tools (RSAT) feature on Windows 7

In order to install the Active Directory Module for Windows PowerShell you need to download the RSAT tools for Windows 7 here. Once this is installed you are still not finished, you need to enable the Active Directory module. Navigate to Control Panel > Programs and Features > Turn Windows Features On or Off and select Active Directory Module for Windows PowerShell that you want to install.

Once you have Active Directory web services running on at least one domain controller and the AD PowerShell module is installed, you are ready to run the AD PowerShell module. You can do this in one of two ways. First, you can access the “Active Directory Module for Windows PowerShell” shortcut in Administrative Tools as shown here:

Right click the shortcut and select “Run as administrator” in order to start PowerShell with elevated permissions.

You can also simply import the AD PowerShell module in your existing PowerShell session. Just use the Import-Module ActiveDirectory command:

Import-Module ActiveDirectory

PowerShellPack Modules
http://archive.msdn.microsoft.com/PowerShellPack

Windows PowerShell Pack contains 10 modules to help supercharge your Windows PowerShell scripting. The PowerShellPack lets you write user interfaces in PowerShell script, manage RSS feeds, schedule operating system tasks, and much more. Download and install PowerShell pack to get started.

Run Import-Module PowerShellPack from within PowerShell.

Module Description
WPK – Create rich user interfaces quick and easily from Windows PowerShell. Think HTA, but easy. Over 600 scripts to help you build quick user interfaces
TaskScheduler – List scheduled tasks, create or delete tasks
FileSystem  – Monitor files and folders, check for duplicate files, and check disk space
IsePack  – Supercharge your scripting in the Integrated Scripting Environment with over 35 shortcuts
DotNet  – Explore loaded types, find commands that can work with a type, and explore how you can use PowerShell, DotNet and COM together
PSImageTools  – Convert, rotate, scale, and crop images and get image metadata
PSRSS  – Harness the FeedStore from PowerShell
PSSystemTools  – Get Operating System or Hardware Information
PSUserTools  – Get the users on a system, check for elevation, and start-processaadministrator
PSCodeGen  – Generates PowerShell scripts, C# code, and P/Invoke

MSDN Powershell Projects

There are many PowerShell projects with any imaginable PowerShell modules for download. Just visit the site  at http://archive.msdn.microsoft.com/Project/ProjectDirectory.aspx?ProjectSearchText=Powershell and browse away.

Perform In-Place file edit with VBScript

Recently had the need to search and replace a configuration file on a system. This was part of an installation of an older program that required a variable to be replace with the computername. Being that the systems were Windows of several different releases (2000, 2003 and 2008) I could not use Powershell to complete the task and went with vbscript as the tool of choice.

Here is what the script looks like. In this example I am editing the config.ini file and looking for WIN01 and replacing it with the computername.

Const ForReading = 1
Const ForWriting = 2
Const FileIn = "c:\oldasdirt\bin\config.ini"
Const FileOut = "c:\oldasdirt\bin\config.ini" 

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(FileIn, ForReading)
Set wshShell = WScript.CreateObject( "WScript.Shell" )

strComputer =wshShell.ExpandEnvironmentStrings( "%COMPUTERNAME%" )

strText = objFile.ReadAll
objFile.Close
strNewText = Replace(strText, "WIN01", strComputer)

Set objFile = objFSO.OpenTextFile(FileOut, ForWriting)
objFile.WriteLine strNewText
objFile.Close

The script can be broken done into several sections.

  • define constants
  • set objects
  • set variables
  • do the work
  • close it up

For define constants we need to define the file for Reading and Writing and also define the input and output file.

Const ForReading = 1
Const ForWriting = 2
Const FileIn = "path to input file"
Const FileOut = "path to output file"

There are three File input / output constants available as shown in the table below.

Constant Value Description
ForReading 1 Open a file for reading only. No writing to this file can take place.
ForWriting 2 Open a file for writing. If a file with the same name exists, its previous contents are overwritten.
ForAppending 8 Open a file and write to the end of the file.

For FileIn and FileOut we just need to put the location and name of the file we want to edit.

Next we need to set the file system object , set the File object and set the shell environment.

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(FileIn, ForReading)
Set wshShell = WScript.CreateObject( "WScript.Shell" )

Now we want to create a variable called strComputer and get the Environment variable for %COMPUTERNAME%.

strComputer =wshShell.ExpandEnvironmentStrings( "%COMPUTERNAME%" )

Next we need to open the file we want to edit into memory and then create a variable for finding and replacing the replacement text.

strText = objFile.ReadAll
objFile.Close
strNewText = Replace(strText, "WIN01", strComputer)

And finally, lets write the changes to the file.

Set objFile = objFSO.OpenTextFile(FileOut, ForWriting)
objFile.WriteLine strNewText
objFile.Close

And that’s all there is to editing a file in place with vbscript. Hope you find this useful to your situation.

 

Using Powershell to Parse Event Logs

Powershell is a well needed addition to Windows. For a longtime there has been Windows admins wanting a powerful and flexible scripting language similar to what Linux / Unix users have had for years, or at least I have.

One of the great command line tools for Linux has been the use of commands like cat, less, more and grep as a few to parse files. Enter in Powershell for Windows. A recent need for parsing the application event log for specific event id 3001 and ASP .NET errors causing an issue with SharePoint servers. With Powershell we can achieve this by using the get-eventlog cmdlet and the where statement to look for the match.

As part of the requirements to the script I wanted to have the ability to put a limit on how far back to look in the application log, write it to a file and if it finds an event that occurred today then send me an email. Here is what came out of it.

# parselogevent.ps1
# Checks for events less than 7 days that occur today and emails.
#

del C:\logs\lastevent.txt

$Now = Get-Date
$SubtractDays= New-Object System.TimeSpan 7,0,0,0,0
$Then = $Now.Subtract($SubtractDays)

$lastevent_log=get-eventlog application -after $Then -before $Now | where {$_.Message -match “3001″ -and $_.Source -match “ASP.NET 2.0.50727.0″} | format-table -wrap -autosize -property TimeWritten, Message > C:\Support\lastevent.txt

$lastevent_file=[io.file]::ReadAllText(‘C:\logs\lastevent.txt’)

$files = “C:\logs\lastevent.txt”

$exist = test-path $files

$today = (get-date).ToShortDateString()

$file = get-childitem $files

if($file.length -gt 0){
$emailFrom = “myemail@myhost.com”
$emailTo = “theman@theman.com”
$subject = “Timeout Error  $env:COMPUTERNAME”
$body = $lastevent_file
$smtpServer = “my.smtp.com”
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($emailFrom, $emailTo, $subject, $body)
$lastevent_log.TimeWritten|Set-Content C:\logs\lastevent.txt
}

Now lets explain how it works!!!

First line we want to delete any log exports from before

del C:\logs\lastevent.txt

Now we need to set the variables for date, math to set 7 day and then calculate it all.

$Now = Get-Date
$SubtractDays= New-Object System.TimeSpan 7,0,0,0,0
$Then = $Now.Subtract($SubtractDays)

Now the meat. Lets parse the log an find event id 3001 with message containing ASP.NET 2.0.50727.0 and export to lastevent.txt.

$lastevent_log=get-eventlog application -after $Then -before $Now | where {$_.Message -match “3001″ -and $_.Source -match “ASP.NET 2.0.50727.0″} | format-table -wrap -autosize -property TimeWritten, Message > C:\Support\lastevent.txt

At this point you could look at the exported file and see if you have any events. But we are after automation here, as we are good admins.

Next we need to create some variables to use for formatting and sending the email.

$lastevent_file=[io.file]::ReadAllText(‘C:\logs\lastevent.txt’)

$files = “C:\logs\lastevent.txt”

$exist = test-path $files

$today = (get-date).ToShortDateString()

$file = get-childitem $files

And finally the email piece, this is pretty straight forward and doesn’t need a lot of explaination as the descriptions speak for themselves. You will need to change the values for $emailFrom, $emailTo, $subject & $smtpServer.

if($file.length -gt 0){
$emailFrom = “myemail@myhost.com”
$emailTo = “theman@theman.com”
$subject = “Timeout Error  $env:COMPUTERNAME”
$body = $lastevent_file
$smtpServer = “my.smtp.com”
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($emailFrom, $emailTo, $subject, $body)
$lastevent_log.TimeWritten|Set-Content C:\logs\lastevent.txt
}

Save your finished file and give it a run. If it works, then be a good admin and create a scheduled task and move on to the next.

If you want to save a little typing time just download the parselogevent.

 

Using Powershell to check service and send email

Ran into an issue with services stopping for no reason and causing IIS to stop. This was happening in the middle of the night and was sporadic in nature, making it hard to find a root cause. As a band-aid I created a Powershell 2 script to check for the service, restart if not in a running state and send and email.

Here is the script.

# Checks for a service to be running and starts if needed.
# Author: Me
# Date: 01/16/2012
# Name: checksvculti1.ps1
# must use a wrapper script with powershell script due to running at privileged access.

function FuncCheckService{
param($ServiceName)
$arrService = Get-Service -Name $ServiceName
if ($arrService.Status -ne "Running"){
Start-Service $ServiceName
FuncMail -To "myemail@yourhost.com" -From "myserver@yourhost.com"  -Subject "Servername : ($ServiceName) service started." -Body "Service $ServiceName started" -smtpServer "mysmtpserver.com"
}
}

function FuncMail {
param($To, $From, $Subject, $Body, $smtpServer)
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.From = $From
$msg.To.Add($To)
$msg.Subject = $Subject
$msg.IsBodyHtml = 1
$msg.Body = $Body
$smtp.Send($msg)
}

FuncCheckService -ServiceName "IISADMIN"

the script is pretty simple and really has two defined functions FuncCheckService and FuncMail. the last line of the script runs FuncCheckService  and adds the parameter -ServiceName where you define the service you want to check.

Now to modify this script you only have to change a few settings.

The line in FuncCheckService that calls the FuncMail you will need to change

  • -To
  • -From
  • -Body
  • -smtpServer

And the last line of the script is where you will define the service to check. Change value after -ServiceName to the service you want to check.

If you notice in the comments for the script I mention that is must be run in a wrapper script. The reason is do to privileges need to be run. I am running this script in Unrestricted mode. The wrapper script looks like this.

powershell.exe -command "Set-ExecutionPolicy Unrestricted -force"
powershell.exe c:\Scripts\checksvculti1.ps1

Save this file and the Powershell script with whatever names you like and in the same directory. You can now schedule this to run as a scheduled task.

Switch to our mobile site