Friday, August 31, 2007

Microsoft adds remote management In PowerShell 2.0

 

"We've been pretty up front that the need of IT admins is [for remotely administering machines]. It's a new model for [remote management] to 10 machines, 100 machines, maybe even 5,000 machines. So basically, 'remoting' will be built into the heart of the PowerShell engine and then exposed as a set of language features and commands versus 'remoting' being done as a bolt-on afterthought. People who in the past were intimidated by 'remoting' or found it difficult will find this simple."


Read the complete interview with Jeffrey Snover on SearchWinIT.com.

CAN'T WAIT!

Query HTTP status codes and headers with PowerShell


I wrote this after a posting it in the PowerShell newsgroup. Three methods to the task:


1. Xml Http (com object)

$url = "http://www.cnn.com"
$xHTTP = new-object -com msxml2.xmlhttp;
$xHTTP.open("GET",$url,$false);
$xHTTP.send();
$xHTTP.ResponseText; # returns the html doc like downloadstring
$xHTTP.status # returns the status code

 

PS C:\Scripts> $xHTTP.getAllResponseHeaders()
Date: Fri, 31 Aug 2007 19:05:39 GMT
Server: Apache
Accept-Ranges: bytes
Cache-Control: max-age=60, private
Expires: Fri, 31 Aug 2007 19:06:31 GMT
Vary: Accept-Encoding,User-Agent
Content-Encoding: gzip
Content-Length: 30296
Content-Type: text/html
Keep-Alive: timeout=5, max=64
Connection: Keep-Alive

PS C:\Scripts> $xHTTP.getResponseHeader("Content-Length")
30296
PS C:\Scripts> $xHTTP.status
200
PS C:\Scripts> $xHTTP.statusText
OK

# $xHTTP.responseText will return the document text.

 

 

2. System.Net.HttpWebRequest

$url = "http://www.cnn.com"
$req=[system.Net.HttpWebRequest]::Create($url);
$res = $req.getresponse();
$stat = $res.statuscode;
$res.Close();

 

PS C:\Scripts> $res

IsMutuallyAuthenticated : False
Cookies : {}
Headers : {Vary, X-Pad, Keep-Alive, Connection...}
ContentLength : 135531
ContentEncoding :
ContentType : text/html
CharacterSet : ISO-8859-1
Server : Apache
LastModified : 8/31/2007 7:01:46 PM
StatusCode : OK
StatusDescription : OK
ProtocolVersion : 1.1
ResponseUri : http://www.cnn.com/
Method : GET
IsFromCache : False
PS C:\Scripts> $res.Headers
Vary
X-Pad
Keep-Alive
Connection
Accept-Ranges
Content-Length
Cache-Control
Content-Type
Date
Expires
Server

PS C:\Scripts> $res.GetResponseHeader("Content-Length")
135531

-or-  $res.<HeaderString>

$res.ContentLength
135531

PS C:\Scripts> $res.StatusCode
OK
PS C:\Scripts> $res.StatusDescription
OK

 

3. System.Net.WebClient

$url="http://www.cnn.com"
$wc = new-object net.webclient
$html = $wc.DownloadString($url)

 

There's is still a lot to investigate on this classes and its methods and this post can be only the tip of the iceberg. Personally, I prefer to use the System.Net.WebClient DownloadString method. It's quick and short to type. I don't want to rely on any methods to query responses based on status codes.
In my scripts I choose to get the document text and query it for a matching string.
I do it because if the target web server redirects you (which happens in most cases) to another page using the Server.Transfer method (e.g., 404 - page not found) than you
wont get 404, you'll get back "OK" or 200.

 

Here is another example. Frequently, I need to check if one of my web server on the Internet is listed in CBL's (Composite Blocking List) database as a spam source. This site is one of the most important spam databases. If you're server is listed there it is more likely that you are listed on numerous spam databases on the web. Most of them will simply query CBL.

 

$ip = "<WebServerIP>";
$url = "http://cbl.abuseat.org/lookup.cgi?ip=$ip";
$lookup = "^IP Address $ip was not found.*";
$wc = (new-object net.webclient).DownloadString($url);

if($wc -match $lookup){
    "Listed";
} else{
    "Not Listed";
}

 

Enjoy,

Shay

Introduction to scripting PowerShell for Exchange 2007


Great introduction to scripting PowerShell for Exchange 2007 by Evan Dodds
Program Manager at Microsoft Corporation. Watch the video on IT's ShOwtime!.


Session description

"This session covers the new Windows PowerShell-based Exchange cmdline and scripting interface. Learn how to convert your multiple page Visual Basic and COM scripts to mere one-liners in Exchange 2007. We cover the basics of the management shell, as well as the underlying design and key concepts. Additionally, we go into more depth on how to build larger scripts that can be used to automate small, medium, as well as enterprise business scenarios."

  • Introduction to Powershell Scripting
  • Basics of Powershell and Exchange
  • Demo: Net Objects
  • Demo: Finding Your Way
  • Putting it All Together

List users with email forwarded in Exchange 2007

 

Here's the 2007 on-liner version of Bharat Suneja's post on ExchangepediaBlog. He Explains how to list all users with email being forwarded to another recipient. The sample in file is vbs and is ~35 lines long.

>> get-mailbox | where {$_.ForwardingAddress} | select name, ForwardingAddress, DeliverToMailboxAndForward | format-table -autosize

 

Name                 ForwardingAddress               DeliverToMailboxAndForward
----                    -----------------                      --------------------------
Administrator     homelab.loc/Users/Shay       True
User1                 homelab.loc/Users/User2     False

 

PowerShell is sooooooooo COOL!!!

Thursday, August 30, 2007

Who logged on to my mailbox?

 

This on-liner lists all mailboxes and the user account that was the last to open it.

get-mailbox  | select name, @{n="LastLoggedOnUser";e={(Get-MailboxStatistics -id $_).LastLoggedOnUserAccount}}

 

Name               LastLoggedOnUser
------                ----------------------
Administrator   HOMELAB\Administrator
Shay                 HOMELAB\Shay
User1               HOMELAB\Administrator
User2               HOMELAB\User2
User3               HOMELAB\User3

 

As you can see, the Administrator account was the last user to open User1's mailbox.

 

UPDATED:

Actually here is a better one :)

# ? = where-object
# ft -a = format-table -autosize

>> Get-MailboxStatistics | ? {$_.objectclass -eq "mailbox"} | select DisplayName,Last* | ft -a

 

DisplayName   LastLoggedOnUserAccount   LastLogonTime              LastLogoffTime
-----------        -----------------------             -------------                    --------------
Administrator HOMELAB\Administrator       30/08/2007 12:32:40    30/08/2007 12:51:40
Shay               HOMELAB\Shay                    30/08/2007 12:13:11    30/08/2007 12:43:20
User1             HOMELAB\Administrator       18/05/2007 00:33:40    18/05/2007 00:33:40
User2             HOMELAB\User2                   18/05/2007 00:32:40    18/05/2007 00:32:40
User3             HOMELAB\User3                   18/05/2007 00:31:40    18/05/2007 00:31:40

Email address validation with PowerShell and Exchange 2007


This function verifies if a string is a qualified email address.

function Validate-EmailAddress{
    param([string]$email)
    [Microsoft.Exchange.Data.SmtpAddress]::IsValidSmtpAddress($email);
}


>> Validate-EmailAddress "Shay@homelab.loc"
True

>> Validate-EmailAddress "Shay.homelab.loc"
False

Wednesday, August 29, 2007

Changing SMTP primary address (Exchange 2007)

 
This function will list all Email addresses for a given user and allow you to choose (interactively) a new default SMTP address. Use the -Verify switch to emit the new default address.
 
 

function Set-PrimaryEmailAddress{
    param([string]$name, [switch]$verify)
    # filters only smtp addresses
    $smtp =  @((get-Mailbox -Identity $name).EmailAddresses | where {$_.PrefixString -eq "SMTP"})

    for($i=0;$i -lt $smtp.length;$i++){
        if($smtp[$i].IsPrimaryAddress){
            # colorize default address in green
            write-host "$($i+1). $($smtp[$i].SmtpAddress)" -b black -f green;
        } else {
            write-host "$($i+1). $($smtp[$i].SmtpAddress)";
        }
    }
    [int]$num=0;
    $choice=read-host "`nChoose new primary SMTP address for <$name>?"
    if((-not [int]::TryParse($choice,[ref]$num) -or $num -gt $smtp.length)){
        write-host "ERROR: Invalid input." -foreground red -background black;
        exit;       
    } else {
        # you need to assign $false to -EmailAddressPolicyEnabled otherwise an error occurs
        #Set-Mailbox : This command will clear the PrimarySMTPAddress property.
        #Because EmailAddressPolicyEnabled is set to true, this action is not allowed.
        set-mailbox -Identity $name -EmailAddressPolicyEnabled:$false -PrimarySmtpAddress $smtp[$num-1];
        if($verify) {(get-Mailbox -Identity $name).PrimarySmtpAddress.ToString()}
    }
}

 

Usage:

>> Set-PrimaryEmailAddress "administrator" -verify

 
 


Tuesday, August 28, 2007

PowerShell for Exchange 2007 SP1


The title is a new book called Professional PowerShell for Exchange 2007 SP1 (This title has not yet been released) by Joel Stidley from Wrox publication.


Book Description
In Wrox’s Professional PowerShell for Exchange 2007 SP1, readers are treated to a guided tour of PowerShell from an Exchange administrator’s perspective as veteran Exchange experts Joel Stidley, Brendan Keane, Jeffrey Rosen, Jonathan Runyan and Joezer Cookey-Gam skillfully guide readers through all the ins and outs of PowerShell, and show them how leverage its power to streamline Exchange 2007 implementation and administration.  Coverage includes:

  • Using PowerShell to Deploy Exchange Server 2007
  • Working with User and Group Objects
  • Working with Public Folders
  • Configuring the Client Access server Role
  • Configuring the Mailbox server role
  • Working with SCC, CCR and SCR
  • Working with Exchange routing
  • Automating administration

Be sure to check it when its out.

Monday, August 27, 2007

Content aware image resizing


Check out this video, amazing stuff.

Things that makes your life easier

PowerShell Community Extensions has a powerful utility called echoargs.exe. This file can help you "see" how the target file/function processes the arguments sent to it, thus troubleshooting any argument related errors.

One such case discussed in the PowerShell Newsgroup, where Keith explained what was the problem (You can find a copy of the post here).

I couldn't resist, so I fired PowerShell :)


function echo-args{
    for($i=0;$i -lt $args.length;$i++){
        "Arg $i is <" +$args[$i]+">";
    }
}

>> echo-args -n -betfsboot.com iso WinPE2.iso

Arg 0 is <-n>
Arg 1 is <-betfsboot>
Arg 2 is <.com>
Arg 3 is <iso>
Arg 4 is <WinPE2.iso>

 

Enjoy!

Validating IP addresses

The common way to validate IP addresses is with Regular expressions. This method helps determine whether a string is a valid IP address. It uses the Parse method of the system.net.ipaddress NET class.

The function returns a boolean value with respect to the tests result.

 

function Validate-IPAddress{
    param([string]$ip=$(throw "Missing IP address argument"))
    trap{$false; continue;}
    [bool][system.net.ipaddress]::Parse($ip);
}

 

 

# Some usage examples

>> Validate-IPAddress "192.168.1.27"
True

Validate-IPAddress "192.168.1.274"
False

 

# common if/else testing
>> $ip="192.168.1.27";
>> if(Validate-IPAddress $ip){
>> "$ip is Valid";
>> } else {
>> write-warning "$ip is NOT Valid";
>> }

192.168.1.27 is Valid

 

# test for false value
# -not logical operator can be replaced with "!"

>> $ip="192.168.1.274";
>> if(-not (Validate-IPAddress $ip)){
>> write-warning "$ip is NOT Valid";
>> }

WARNING: 192.168.1.274 is NOT Valid

How to convert C# forms to PowerShell

 

Check out this post from Doug's blog. Among some other PowerShell posts Doug explains how to convert a simple C# forms into PowerShell script.

Sunday, August 26, 2007

Exchange 2007 Transition - First impressions


Today I finished installing Exchange 2007 to coexist with Exchange 2003. The installation process went fine except for two issues regarding connectors. 

The environment contains one Exchange 2003 (which is also a DC/GC, BAD THING TO DO, Inherited). Here are the steps taken: 

  1. Make sure the Domain functional level is Windows server 2003.
  2. Switch the legacy Exchange organization (2003) to Native mode.
  3. Suppressing link state updates - via registry.
  4. Extending Active directory scheme. I could install Ex2007 and let setup make all necessary changes, but preferred to do the process manually. this includes:
    • Run setup.exe /PrepareLegacyExchangePermissions.
    • Run setup.exe /PrepareScheme.
    • Run setup.exe /PrepareAD.
    • Run setup.exe /PrepareDomain.
  5. Installing Exchange 2007 with the following Roles in command-line mode:

    Note: During setup accept the Routing connector creation dialog. There is also the option to install Exchange from command line:
    >> Setup /mode:Install /roles:HT,CA,MB,MT /EnableLegacyOutlook /on:<OrgName>
    • Hub Transport role (HT).
    • Client Access Role (CA).
    • Mailbox Role (MB).
    • Management tools (MT).
    • /EnableLegacyOutlook. Creates a Public Folder database on the Exchange 2007 server for sharing of Free/Busy calendar information using the legacy Outlook client.
  6. Moved all database stores to another physical disk.
  7. Replicating public folders to Exchange 2007.
  8. Installing latest Exchange Rollup (v4).

 

When setup finished I moved one mailbox to the 2007 server and tested it with Outlook 2003. All looked fine but no mail could be send or received even though nothing was on the Outbox folder. All messages were stuck in the Queue.

Checked the Application log and found:

 

Event Type:    Warning
Event Source:    MSExchangeTransport
Event Category:    Routing
Event ID:    5006
Date:        8/26/2007
Time:        16:13:52
User:        N/A
Computer:    ServerName
Description:
Cannot find route to Mailbox Server CN=SERVER,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=First Organization,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=domainName,DC=com for store CN=UsersStore,CN=First Storage Group,CN=InformationStore,CN=ServerName,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=First Organization,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=domainName,DC=com in routing tables with timestamp 26/08/2007 13:13:52.  Recipients will not be routed to this store.

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

 
I suspected that the creation of the Routing connector during setup (Phase 5 note) didn't succeeded. I launched PowerShell and searched for cmdlets that contain the word "Routing" in its name.

PS C:\Scripts> get-command *routing*

CommandType     Name
-----------     ----
Cmdlet          Get-RoutingGroupConnector
Cmdlet          New-RoutingGroupConnector
Cmdlet          Remove-RoutingGroupConnector
Cmdlet          Set-RoutingGroupConnector

 

I Executed Get-RoutingGroupConnector and nothing returned. 
Then I executed New-RoutingGroupConnector and mail started flowing internally.

>> New-RoutingGroupConnector -Name "InternalRGC"
-SourceTransportServers "Ex07SRV"
-TargetTransportServers "Ex03SRV" 
-Bidirectional $true

To redirect inbound mail from the Internet to the 2007 server I changed the "Permission Group" settings on the Default server receive connector (under Server Configuration node > Hub Transport ) to allow Anonymous users to connect via SMTP and thus internal users to receive emails from outside the organization. Then I issued the following:

>> New-SendConnector -Name "Internet Mail Connector" -AddressSpaces "*"

 

OOOOfffffff <sigh>, "Out of the box" I expected setup to create the Routing group for internal and external use, and that mail flow will behave transparently, but hey... Now I have 381 more cmdlets to work with, CANT WAIT!.

 

Shay

Saturday, August 25, 2007

Restart your engine - The PowerShell way (updated)


This is an update to my first blog post. Now it will open PowerShell via its shortcut file.
Although the first version worked fine, it fired PowerShell with a black background and the window looked very similar to the old DOS prompt. 

You can customize the Windows PowerShell shortcut file (under Start > Programs > Windows PowerShell 1.0 ) to your favorite working folder, colors, dimensions etc. Include the function in your $PROFILE and type "rps" to restart.


function Restart-PowerShell {
$cmd = @"
'This is a temporary script to Restart a PowerShell Session
'Created $(Get-Date)
On Error Resume Next
Set oApp = CreateObject("Shell.Application")
Set oShell = CreateObject("WScript.Shell")
lnk = "%ALLUSERSPROFILE%\Start Menu\Programs\Windows PowerShell 1.0"
PoSH = oShell.ExpandEnvironmentStrings(lnk)
WSCript.Sleep 1000
oApp.Namespace(PoSH).ParseName("Windows PowerShell.lnk").InvokeVerb("Open")
"@

out-file $env:temp"\Restart-PowerShell.vbs" -inputobject $cmd -encoding ASCII -force;
WScript.exe $env:temp"\Restart-PowerShell.vbs";
exit;
}


set-alias rps Restart-PowerShell

Who owns that email address?


Here is a one-liner to find which user owns a specific SMTP address in Exchange 2007.


>> (Get-Mailbox | ? {$_.EmailAddresses -match "^smtp:shay@homelab.loc"}).name
Shay Levi

And as a function


function Get-EmailOwner{
    param([string]$pat)
    $person = (Get-Mailbox | ? {$_.EmailAddresses -match "^smtp:$pat"}).name
    if($person) {$person} else{"No 1"}
}

Wednesday, August 22, 2007

Launching applications in another culture


A while ago I searched a way to launch Windows Live Write (WLW) with en-US culture. WLW doesn't enable spelling in systems that was not configured to use the en-US culture, Hebrew in my case. Roy Osherove wrote a little utility to overcome this. You can read about it here.


In his blog Fredrik asked for help about launching PowerShell with en-US culture. I think the same can be applied to Fredrik's problem. Based on the code Roy wrote, I'm launching WLW from within PowerShell with this:


$wlw = "C:\Program Files\Windows Live Writer\WindowsLiveWriter.exe";
[System.Threading.Thread]::CurrentThread.CurrentCulture = "en-US";
[System.AppDomain]::CreateDomain($wlw).ExecuteAssembly($wlw);

Some more help on AppDomain class on MSDN

New PowerShell videos on Channel 9

 

Two new PowerShell videos just released on Channel 9:

Using Quest's PowerGUI:
Jeffrey Snover and Dmitry Sotnikov: Learn and Master Windows PowerShell with Quest Software’s PowerGUI

Demonstration of Windows PowerShell and Windows Server 2008:
Using Windows PowerShell to manage Windows Server 2008 Active Directory and Read-Only Domain Controllers

Shay

PowerShell Wallpaper (updated)

 

Added two more file sizes (1024x768, 1600x1200) as requested. Check out the original post.

Thanks all for your comments.

 

Cheers,

Shay

Tuesday, August 21, 2007

SMTP Log parsing


From time to time I need to check my IIs SMTP log files to find why a particular user didn't get email notifications. I have several web servers running community web applications that sends tips, notifications and newsletter to subscribed users.

Digging into SMTP logs is a time consuming process. In addition, The SMTP service sends hundreds of emails and tracking a specific session can be quiet a challenge.

I found that a session can be scattered around the log in a non-consecutive order. I needed a way to collect, group and display SMTP sessions lines in a way that I can see the session flow along with its SMTP verbs.

The natural candidate for log parsing was Microsoft's LogParser 2.2. Boy, this tool is something. After experimenting with log parser I automated the process via PowerShell.

The script query's SMTP log files, in W3C format, and displays the output for all sessions per server IP it finds or for the email address specified in $smtpAddress parameter. I added the option to show all sessions in order to find additional error messages occurred for the queried server.

 

 

############# Parse-W3C.ps1 ###############

param(
    [string]$smtpAddress = $(throw "Please specify email address"),
    [string]$log = $(throw "Please specify log file path, accepts wildcard")
)

#cls
$smtpAddress = "TO:<$smtpAddress>";

 

# the user may have more than one server associated, get list of all mail servers ips
$query="SELECT DISTINCT c-ip FROM $log WHERE cs-uri-query LIKE '%$smtpAddress%'";

$ips = logparser -i:W3C -q:ON $query;

if($ips.length -eq 0){
    write-host "Address not found.`n" -b black -f red;
    return;
}

 

# foreach ip returned emit the session

$ips | foreach {

    $query = "SELECT date,time,c-ip,cs-method,cs-uri-query FROM $log WHERE c-ip = '$_'";
    write-host "[QUERY] $query" -b black -f green;

    $lp=logparser -i:W3C -q:ON $query;


    # set start anchors where line matches "220+" (session starts)
    $session=@();   
    for($i=0;$i -le $lp.length;$i++){if($lp[$i] -match "-\s+220\+"){$session+=$i}}
    $session+=$lp.length;

    if($session.length -eq 0){
        write-host "`nNo sessions found for <$_>`n" -b black -f red;
        return;
    }

    $key = read-host "Found $($session.length) sessions for <$_>`n.Display All Sessions [A], User Sessions [U]?";
    write-host;

    for($i=0;$i -lt $session.length-1;$i++){
        # slice array
        $tmpArr = $lp[$session[$i]..$($session[$i+1]-1)];


        # regex to match any of 421,450,451,452,500,501,
        # 502,503,504,550,551,552,553,554 smtp error codes
        $regex =  "(-\s+)?(421|45[0-2]|5(0|5)[0-4])\+";


        # generic code to colorize SMTP errors
        [scriptblock]$paintRow = {
            $tmpArr | % {
                $email = [regex]::match($_,$smtpAddress).success;
                $err = [regex]::match($_,$regex).success;
                if($email){
                    if($err){write-host $_ -f red -b black}
                    else{write-host $_ -f yellow -b black}
                }
                elseif($err){write-host $_ -f red -b black}               
                else{$_}
            }
        }

        if($tmpArr -match $smtpAddress -and $key -eq "u"){
            & $paintRow;
            if($i -eq 0){read-host "`nPress <ENTER> key to continue"} else{write-host}
        }           
        elseif($key -eq "a"){
            & $paintRow;
            if($i -lt $session.length-2){read-host "`nPress <ENTER> key to continue"} else{write-host}
        }
    }
}

 

###########################################

 

Hope you'll find it useful.


Shay

PowerShell Wallpaper


Playing around with Photoshop, I created this wallpaper for my own laptop and to show my support for PowerShell. It's 1280x800 pixels.
 
If you like it, and you want one, drop me a line and I'll try to make one for your resolution.




 
Enjoy,

Shay
 






 

How to Retrieve Remote MAC Address

 

Triggered by Lance Robinson blog post, here is another (quick & dirty, not heavily tested) way to get remote MAC address:

 

function Get-MacAddress{

    param( [string]$ip= $(throw "Please specify IP number") )

    arp -d; # clear arp cache
    $ping = (new-object System.Net.NetworkInformation.Ping).Send($ip);
    $mac = arp -a;
    if($ping){
        ($mac | ? {$_ -match $ip}) -match "([0-9A-F]{2}([:-][0-9A-F]{2}){5})" | out-null;
        $matches[0];
    }
}

 

 

The manual version:

1. Ping the remote machine.
2. Check the local ARP table for a match.

 

C:\WINDOWS>ping 10.0.0.101

Pinging 10.0.0.101 with 32 bytes of data:

Reply from 10.0.0.101: bytes=32 time=2ms TTL=128
Reply from 10.0.0.101: bytes=32 time=1ms TTL=128
Reply from 10.0.0.101: bytes=32 time=1ms TTL=128
Reply from 10.0.0.101: bytes=32 time=1ms TTL=128

Ping statistics for 10.0.0.101:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 1ms, Maximum = 2ms, Average = 1ms

C:\WINDOWS>arp -a

Interface: 10.0.0.105 --- 0x4
  Internet Address      Physical Address      Type
  10.0.0.101            00-09-6b-c6-ab-29     dynamic
  10.0.0.138            00-16-e3-37-d2-47     dynamic

 

Shay

Monday, August 20, 2007

Are you a Poshoholic?

 

A Simple 7-Question Quiz designed To Help You Decide.

Check out Dmitry's blog.

 

Enjoy,

Shay

Sunday, August 19, 2007

Creating Environment variables


To list all environment variables you dir the env: PSdrive


dir env:

-or-

>> [Environment]::GetEnvironmentVariables();



# truncated list

Name                 Value
----                     -----
Path                       C:\Program Files\Windows Resource Kits\Tools\;C:\Program F
SESSIONNAME         Console
PATHEXT                .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.PSC1;.VB
....
....
....
ALLUSERSPROFILE   C:\Documents and Settings\All Users
OS                          Windows_NT
HOMEDRIVE             C:




Creating Variables

Variables can be created for the current process, user or machine context.
Process variables typically will be lost once the process exists (volatile), while user or machine are permanent and stored in the registry.

You can use the .NET SetEnvironmentVariable Method to create , modify or delete an environment variable. The method syntax is as follows:


[Environment]::SetEnvironmentVariable(<name>,<value>,<EnvironmentVariableTarget>);



EnvironmentVariableTarget can hold one of the following constants:
Process = 0
User = 1
Machine = 2

All Italic lines were copied from the SetEnvironmentVariable method web page.



Process variable


"If the target parameter is not Process, the environment variable is not automatically copied to the current process."

Correct me if I'm wrong but I think the "is not" should be "is". Listing the current process environment items shows the new created "Shay" item. Anyway, here is the create process:


>> [Environment]::SetEnvironmentVariable("Shay","Levi","Process");


User variable


"If target is User, the environment variable is stored in the Windows operating system registry key reserved for the current user, and is copied to the current process when the user who stored the variable starts a new process."

>> [Environment]::SetEnvironmentVariable("Shay","Levi","User");


Screen shot taken from the System properties applet



Machine variable


"If target is Machine, the environment variable is stored in the Windows operating system registry key reserved for the local machine, and is copied to the current process when any user on the local machine starts a new process."


>> [Environment]::SetEnvironmentVariable("Shay","Levi","Machine");




Delete variables


To delete a variable assign the $null to the item value

[Environment]::SetEnvironmentVariable("Shay",$null,0); # Process
[Environment]::SetEnvironmentVariable("Shay",$null,1); # User
[Environment]::SetEnvironmentVariable("Shay",$null,2); # Machine





FREE PowerShell Stickers

 
/n software offers a couple of PowerShell stickers - one for your car, one for you laptop. You need to fill in a form on their website and you will automatically be entered for a chance to win a new PowerShell Skinned XBox 360.
 
I already got mine. Unfortunately I had a car accident last Thursday (nothing serious) and my car is in repairs, so I can't show off my bumper here )-:
 
Get your own stickers here.
 
 

Saturday, August 18, 2007

Getting System up time (locally)


Here is another way to get your servers up time (total time since last reboot).
This method works locally.

The .NET Environment class exposes the TickCount property. TickCount Gets the number of milliseconds elapsed since the system started. It's a 32-bit signed integer type and by its restrictions it will be "reset" back to zero every 49.7 days (see Environment.TickCount Property on MSDN).

>> [environment]::tickcount;
297580406

To format the result to something more meaningful we'll use the New-TimeSpan cmdlet.
New-TimeSpan doesn't expose a milliseconds parameter so we'll divide the tickcount result by 1000 and feed the New-TimeSpan -seconds parameter.

>> New-TimeSpan -seconds ([environment]::tickcount / 1000);

Days : 3
Hours : 10
Minutes : 42
Seconds : 42
Milliseconds : 0
Ticks : 2977620000000
TotalDays : 3.44631944444444
TotalHours : 82.7116666666667
TotalMinutes : 4962.7
TotalSeconds : 297762
TotalMilliseconds : 297762000

So, to get only the days since last reboot:

>> (New-TimeSpan -seconds ([environment]::tickcount / 1000)).days;
3

Another formatting variation:

>> $sup = New-TimeSpan -seconds ([environment]::tickcount / 1000);
>> [string]::format("D:{0:00},H:{1:00},M:{2:00}",$sup.days,$sup.hours,$sup.minutes);

D:03,H:10,M:53

But wait, There's more. Browsing to the TimeSpan class page at MSDN reveals that TimeSpan class has a TimeSpan.FromMilliseconds Method.

>> [timespan] gm -static fr*;

TypeName: System.TimeSpan
Name MemberType Definition
---- ---------- ----------
FromDays Method static System.TimeSpan FromDays(Double value)
FromHours Method static System.TimeSpan FromHours(Double value)
FromMilliseconds Method static System.TimeSpan FromMilliseconds(Double value)
FromMinutes Method static System.TimeSpan FromMinutes(Double value)
FromSeconds Method static System.TimeSpan FromSeconds(Double value)
FromTicks Method static System.TimeSpan FromTicks(Int64 value)


Now, we can provide milliseconds to TimeSpan in a "static notation form":

>> $sup = [timespan]::FromMilliseconds([environment]::tickcount);
>> [string]::format("{0:00}:{1:00}:{2:00}",$sup.days,$sup.hours,$sup.minutes);

03:11:04

Note: If the returned values are negative, we can use the [math]::abs method to return absolute values.

Shay

Wednesday, August 8, 2007

Suspected in blog spamming


My Blogger account wont let me post. After logging in I found the following message:

 

#########################

WARNING
This blog has been locked by Blogger's spam-prevention robots. You will not be able to publish your posts, but you will be able to save them as drafts.

Blogger's spam-prevention robots have detected that your blog has characteristics of a spam blog. Since you're an actual person reading this, your blog is probably not a spam blog.
Automated spam detection is inherently fuzzy, and we sincerely apologize for this false positive.

We received your unlock request on August 7, 2007. On behalf of the robots, we apologize for locking your non-spam blog. Please be patient while we take a look at your blog and verify that it is not spam.

##########################

 

Say what??? On behalf of the robots??? The only thing in this blog is code oriented, nothing with spam keywords and the amount of posts is certainly not in a volume of a spam , strange.

Anyway, my unlock request was sent half day ago and still I *cant* publish anything? So how can you read this? Well, it turns out that publishing is still possible using Windows Live writer. Windows live writer, in some way, is bypassing the mechanism used by Blogger.

 

So if you are one of the Blogger team, CLOSE THIS HOLE and let me post!

Shay

Tuesday, August 7, 2007

IP decimal to binary conversion and back

 

Just playing around with IP numbers. here are two PowerShell one-liners for the job.

 

$ip = "192.168.0.1";

# convert to binary form
$sbin=[string]::join(".",($ip.split(".") | % {[System.Convert]::ToString($_,2).PadLeft(8,"0")}))   
$sbin


11000000.10101000.00000000.00000001

# convert the result back to decimal
[string]::join(".",($sbin.split(".") | % {[System.Convert]::ToByte($_,2)}))
192.168.0.1

This one-liners can be converted to functions in order to use them in the pipeline thus converting bulk ip numbers easily and PowerShelly ;)

Exchange 12 Rocks!



I have to prepare my environment for Exchange 2007. Cool, now I'll have the chance to use PowerShell in a more broad scope. In the research on how to coexist Exchange 2003 and Exchange 2007 I came up on this Easter egg thing.


When installing Exchange 2007, the setup creates the Exchange Routing Group and the Exchange Administrative Group with some characters strings that looks very odd.







I Googled for it and found two blog posts (see below) . Well, It turns out to be logical. It's a famous Caesar cipher with offset of 1. Both strings shows the same result. The Exchange Routing Group string should be shifted one char up while Exchange Administrative Group's offset is 1 down.



My fingers started to get itchy, so I fired PowerShell and started coding:

[char[]]'FYDIBOHF23SPDLT' | % {$s=''}{$s+=[string][char]([int][char]$_-1)}{$s}
[char[]]'DWBGZMFD01QNBJR' | % {$s=''}{$s+=[string][char]([int][char]$_+1)}{$s}



Run the commands and watch the output. Now, all I have to say is:
MPOH!MJWF!QPXFSTIFMM



Resources:
Jim McBee's Web Log
MSExchange.org

Shay