Is it Power Management or Power Mis-Management?

So I hear a lot of complaints about network cards getting shut off when users leave their systems for a while.  This apparently drives our local branch techs a little crazy (who can blame them!) so I needed to find a command to add to our imaging process that would fix this issue post deployment.  Unfortunately it can’t be fixed on the image if your are using Sysprep and Mini-Setup to detect and install software drivers.  We use a single image with MDT 2010 and WDS to deploy to over 12 different models. So, while this script looks pretty simple it was a royal pain in my butt to find the solution, then only 15 minutes to write and test.  Oh well.  : /

Set NetworkAdapters = GetObject("WinMgmts://./root/Cimv2")._
ExecQuery("SELECT * FROM Win32_NetworkAdapter WHERE AdapterTypeId=0")
For Each NetworkAdapter in NetworkAdapters
  NetworkAdapterID = UCase(NetworkAdapter.PNPDeviceID)
  Set DevPwrMgtSettings = GetObject("winmgmts://./root/WMI")._
  InstancesOf("MSPower_DeviceEnable",48)
  For Each DevPwrMgtSetting in DevPwrMgtSettings
    DeviceID = UCase(Left_
    (DevPwrMgtSetting.InstanceName, Len(NetworkAdapterID)))
    If NetworkAdapterID = DeviceID Then
      DevPwrMgtSetting.Enable=False
      DevPwrMgtSetting.Put_
    End If
  Next
Next

So, breaking it down: First we get a list of network adapters [Set NetworkAdapters = GetObject(“WinMgmts://./root/Cimv2”).ExecQuery(“SELECT * FROM Win32_NetworkAdapter WHERE AdapterTypeId=0”)]. The AdapterTypeID property is used to select only ethernet adapters. Then we pull the PnPID of the adapter [NetworkAdapterID = UCase(NetworkAdapter.PNPDeviceID)] as this is how the MSPower WMI provider identifies devices. Next build our collection of devices MSPower can access [Set DevPwrMgtSettings = GetObject(“winmgmts://./root/WMI”).InstancesOf(“MSPower_DeviceEnable”,48)]. Then we make the InstanceName property of the MSPower object comparable to the NIC PnPID [DeviceID = UCase(Left(DevPwrMgtSetting.InstanceName, Len(NetworkAdapterID)))]. To make sure we are changing the power management setting on a network adapter and not some other device (USB hub, whatever) we compare the PnPID and InstanceName [If NetworkAdapterID = DeviceID Then] and then do the real work, turning off power management [DevPwrMgtSetting.Enable=False]. The final command [DevPwrMgtSetting.Put_] is extremely important, because without it your setting will not be saved!

Now save this script to a .VBS file and run it using cscript.exe: cscript \.vbs

And that’s it, how to easily turn the Power Management settings on your network adapter off (and on if you change the false to true). It is also possible to manage other settings like WOL using some other components of the MSPower WMI provider.

MED-V – Changing the Workspace Memory pre-deployment

I’ve been working on developing a MED-V workspace for my company.  It’s going to be used to run a single application, and as such, it shouldn’t need much memory.  Some of the systems the workspace will run on will have only 2GB of memory so I don’t want to be using 1GB for the workspace.  I fiddled around for a while trying to find the way to make this change before I started investigating the powershell script in the workspace directory.  If you haven’t run the Workspace Packager yet, this directory won’t exist, but you can copy the commands below into a PS1 and run them yourself.  I would run the Workspace Packager first though so you better understand the options.  In the PS1 file there are a couple of new Med-V specific commandlets used: New-MedvConfiguration,  Export-MedvConfiguration, New-MedvWorkspace, and Export-MedvWorkspace.  Here’swhat my PS1 script looks like (with any company specific info removed, of course!):

Import-module -Name “Microsoft.Medv”

$regFileInfo = New-MedvConfiguration -VmNetworkingMode “BRIDGED” -UxLogonStartEnabled “False” -UxCredentialCacheEnabled “False” -UxRedirectUrls “http://oldsite.mycompany.com” -FtsMode “Attended” -VmMultiUserEnabled “True” -FtsAddUserToAdminGroupEnabled “False” -FtsStartDialogMsg “A virtual environment is being created for application compatibility. The first time setup process can take several minutes to complete.” -FtsFailureDialogMsg “First time setup failed while creating a virtual environment for application compatibility.” -FtsRetryDialogMsg “First time setup failed while creating a virtual environment for application compatibility.” -FtsSetComputerNameEnabled “True” -FtsComputerNameMask “v%hostname%[14]” -FtsSetRegionalSettingsEnabled “True” -FtsSetUserDataEnabled “True” -FtsSetJoinDomainEnabled “True” -FtsSetMachineObjectOUEnabled “True” -VmMemory 512 | Export-MedvConfiguration -Path “C:\Users\heaths\Documents\MyCompany XP VM\MyCompany XP VM.reg” -PassThru

if ($regFileInfo) #If the .Reg file was created, then create the workspace package

{                 New-MedvWorkspace -WorkspaceName “MyCompany XP VM” -VhdFilePath “C:\Users\heaths\AppData\Local\Microsoft\Windows Virtual PC\Virtual Machines\MyCompany XP VM.vhd” -SettingsFilePath “C:\Users\heaths\Documents\MyCompany XP VM\MyCompany XP VM.reg” -VhdRegPath “C:\Program Files\Microsoft Enterprise Desktop Virtualization\MEDV_InternetZoneHighSecurity.reg,C:\Program Files\Microsoft Enterprise Desktop Virtualization\MEDV_RestrictedBrowsingMode_Apply.reg” | Export-MedvWorkspace -Path “C:\Users\heaths\Documents\MyCompany XP VM\MyCompany XP VM.msi” -Overwrite }

OK, that’s a lot of stuff, but let’s look at what’s relevant to this post.  New-MedvConfiguration generates the object that will define your Virtual Machines properties.  Look for -VMMemory.  It’s near the end of the line, but before the Export-MedvConfiguration command.  This setting let’s me change the Virtual Machine’s memoru setting from the default 1024 MB to 512 MB.

So if you are deploying a Med-V Workspace for your company and you’d like to change the default memory setting pre-deployment, that’s how you do it!

UPDATE: I found this blog article that has great info as well.

Windows Server 2003 WDS and 64-bit Imaging

Hopefull all of you are moving rapidly to Windows Server 2008, but for the few people who have not finished updating their environment, this might help.

On servers running 2008 or 2008 R2 all of the architectures of systems being imaged was detected correctly and the x64 boot image was loaded and there was harmony in deploment land.

On servers running 2003 R2 some system would refuse to load the boot image.  After a little research it seems that this option is passed through DHCP options, but isn’t always reliable, as in my case.

On the 2003 R2 WDS server run:

wdsutil /set-server /architecturediscovery:yes

sc stop wdsserver

sc start wdsserver

Viola!  WDS will now confirm the system architecture itself.  This makes the boot process a tad longer and creates a little bit more network traffic, but in my opinion it was negligible.

MDT 2010 and Multiple DNS Namespaces

I ran into an interesting problem over the last couple of week when setting up a Windows 7 deployement infrastructure.  Some simple basics for reference:  I am using a “master” server to replicate my deployment share to roughly 40 servers at different locations using DFSR.  I have set up a domain namespace (\\company.com\deploymentshare) and I have added all of the servers to the namespace.  We have DHCP set up to deliver the DNS suffix “clients.company.com” and our servers reside in the “company.com” DNS namespace.  This seemed to work fine when I tested in our lab and at a few select sites.  When a began to move the solution into other sites and test I experienced extreme lag times in WinPE and once the MDT wizard started I was unable to connect to the deployment share.  This baffled me as it still worked when running a VM on the same Hyper-V server that hosted the WDS server and DeploymentShare.  After some snooping (and using a port span to sniff the traffic since I couldn’t load NetMon in WinPE) I realized that the WinPE client could ping the DNS namespace because if was fully qualified, but it couldn’t reach the actual server and was trying to ARP for the NetBIOS name.  As you know, normal Windows client behavior is to search the parent domain suffixes of the client.  In Windows PE this is not the case.  The solution was to add a command before the MDT scripts began to add a DNS suffix search order.  In order to make this change survive deployment share updates you have to edit the templates that come with MDT.  You can find these under C:\Program Files\Microsoft\Deployment Toolkit\Templates and they will be named Unattend_PE_x64.xml and Unattend_PE_x86.xml.  Open the files and go to the section that starts with <RunSynchronous> and change it to look like the sections below.  Update your deployment share and choose a complete rebuild and your problems are solved!

<RunSynchronous>
  <RunSynchronousCommand wcm:action="add">
    <Description>Set DNS Suffix search order</Description>
    <Order>1</Order>
    <Path>wmic nicconfig call SetDNSSuffixSearchOrder (company.com, clients.company.com)</Path>
  </RunSynchronousCommand>
  <RunSynchronousCommand wcm:action="add">
    <Description>Lite Touch PE</Description>
    <Order>2</Order>
    <Path>wscript.exe X:\Deploy\Scripts\LiteTouch.wsf</Path>
  </RunSynchronousCommand>
</RunSynchronous>

SQL Server Trigger: Updating another table using Inner Joins

I had a situation recently where we were changing an internal process that meant we would no longer be updating the data in a specific table because those column where moving to another table.  Unfortunately there was going to be some crossover and a temporary trigger may be necessary to continue to populate the previous table.  So below we are using an Update statement with Inner Joins.  Another cool feature I had forgotten about is the Inserted and Deleted tables.  We use the Inserted table to make sure we are only updating the rows affected by the Update or Insert statement that initiated the trigger. 

CREATE TRIGGER My_Trigger
ON Table_1
FOR INSERT, UPDATE
AS
   UPDATE Table_2
   SET Table_2.Column_1 = Table_1.Column_1,
       Table_2.Column_2 = Table_1.Column_2
   FROM Table_2
   INNER JOIN inserted
   ON Table_2.PK_1 = Inserted.PK_1
   AND Table_2.PK_2 = Inserted.PK_2
   INNER JOIN Table_1
   ON Table_2.PK_1 = Table_1.PK_1
   AND Table_2.PK_2 = Table_1.PK_2
GO

Collecting Database Information

I mentioned in a previous post that I was going to try and give up my beloved VBScript and work in some more PowerShell.  I’ve been thinking for a while I would make a table to store some information about all the databases that exist across our SQL Servers.  I thought about writing it in VBScript, TSQL, and .NET before settling on PowerShell.  It’s a new language to learn, and while it’s powerful, there is only so much time in the day.  So here it is.  My first full-fledged, ready-for-primetime PowerShell script.
 
First, let’s talk about the script is about.  We need to collect the SQL Server Name, Database Name, SQL version, Database creation date, Data File size, Log File size, and the Database Status.  The easist way to do this is using the .NET classes available through SQL Server Management Objects, known as SMO.
 
The first part of our script loads the SMO namespace and the PowerShell SQL Server Snap-In’s.  At the moment the script only uses Invoke-SQLCmd, but that could be expanded.
 
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | out-null
Add-PSSnapin SqlServerCmdletSnapin100
Add-PSSnapin SqlServerProviderSnapin100
 
Next we create an empty array variable called $DBInfo.  In the script I don’t use it yet, but I have played with using ConvertTo-HTML and Out-File to make a simple website.  I have a table (database_info) in a database we use for DBA information (mgmtdb) on one of our servers.  You would need to make a table somewhere of your liking with columns that match our INSERT statement later in the script.
 
$DBInfo = @()
$serverlist = invoke-sqlcmd -serverinstance "yourserver" -query "select distinct(instance_name) from mgmtdb.dbo.database_info"
 
Once we have our list of servers we can begin stepping through it and connecting to each one. We create the $svr variable and connect it to the current server in the loop.  We then populate the $version variable since it won’t change for any database on this server. Then we step through each database that exists on the server using the $svr.databases collection.
 
ForEach ($server in $serverlist)
{
  $svr = New-Object "Microsoft.SqlServer.Management.SMO.Server" $server.instance_name
  $version = $svr.version
  foreach ($db in $svr.databases)
  {

 

At the beginning of each loop we set the value of $dbsize and $logsize as these properties can be empty if the DB is unavailable. We then check the database status before we attempt to get the size properties.  Because the $db.size property includes data and log files we take the sum of the size properties in the $db.logfiles collection and subtract it from the $db.size property.  We also have to divide the log file size by 1024 because it is listed in KB not MB.  Too bad there’s no datafiles collection!

    $dbsize = "NULL"
   $logsize = "NULL"
    If ($db.status -match "Normal")
 {
   $dbsize = $db.size – ($db.logfiles|measure-object -property size -sum).sum / 1024
   $logsize = ($db.logfiles|measure-object -property size -sum).sum / 1024
 }

Now we create some variables for use in our INSERT statement.  the $object.property variables didn’t seem to jive well with all the quotes, so it was easier to set up some base variables.  We also set the date to a SQL datetime friendly format. And remove any commas from the $db.status property as our SQL INSERT statement won’t like those at all!

    $thedate = get-date -format "yyyy-MM-dd hh:mm:ss"
    $svrname = $svr.name
    $dbname = $db.name
    $dbcreatedate = $db.createdate
    $dbstatus = ($db.status).tostring()
    $dbstatus = $dbstatus.Replace(","," ")

 

This next section is kind of meaning less for the script, but it’s wicked fun.  The properties and variables we’ve created can be hard to format for output.  The general PSObject object take care of this delimma for us.  First we create a new object, $obj.  Then we add  each of our different database properties.  Once we are done we add the object to our $DBInfo array.  During each loop a new object is created and added to the array.  You could use "$DBInfo | Format-Table" to more easily view the information, or "$DBInfo | ConvertTo-HTML | Out-File dbinfo.html" to make a quick web page.  If your already have the web server up an automated daily job could overwrite the page so that interested parties can keep track of their database sizes.  We also use this to see if new databases "pop-up" because of a wiley developer!

    $obj = New-Object PSObject
    $obj | Add-Member NoteProperty -name ServerName -value $svr.name
    $obj | Add-Member NoteProperty -name DatabaseName -value $db.name
    $obj | Add-Member NoteProperty -name SQLVersion -value $version
    $obj | Add-Member NoteProperty -name CreateDate -value $db.createdate
    $obj | Add-Member NoteProperty -name DBSize -value $dbsize
    $obj | Add-Member NoteProperty -name LogSize -value $logsize
    $obj | Add-Member NoteProperty -name DBStatus -value $db.status
    $DBInfo+=$obj
 
The final piece is to build your SQL INSERT statement, then use Invoke-SQLCmd to run it.
    $sqlcmd = "INSERT INTO mgmtdb.dbo.SQL_Web_Data VALUES (‘$svrname’,’$dbname’,’$version’,’$dbcreatedate’,$dbsize,$logsize,’$dbstatus’,’$thedate’)"
    invoke-sqlcmd -serverinstance "yourserver" -query $sqlcmd
  }
}
 
I hope this has provided some new insights into PowerShell for anyone who reads this.  I know writing the script (which took about 2 hours with research) taught me a ton of things I’ll use in future PowerSehll scripts, most notably the use of PSObject for formatting and data manipulation.
 
Have a Happy Thanksgiving!

Concurrent Processing (or faux multithreading)

Have you ever needed to run a command against 2000 workstations and thought, "Man that batch file is going to run forever!"  You might even worry the data will be too stale to matter when it finally gets to you.  I’ve run into this situation multiple times and the fix was to write a quick and dirty app in .NET that uses some simple multithreading.  I just change around some strings, recompile, and I’m off to the races.  Not a programmer you say?  Well, you need to learn a little.  And if .NET isn’t your thing, learn some VBScript and PowerShell.  Next year I plan to start abandoning VBScript <sniff> and moving to PowerShell, but because PowerSehll is not standard on all Windows systems yet it is easier to write i VBScript.  So what I provide for you here is a simple VBScript that will run multiple processes concurrently.  This is not multti-threading, so it uses more resources than my .NET code does, but it’s pretty darn cool.
 
So let’s start with the main piece the Class we create to handle our new process.
 
Class RunCmd
  Private p_Shell
  Private p_Exec
  Private p_Name
  Private p_Available
  Private p_ExitCode
  Public PID
  Public EC_Retrieved
 
  Private Sub Class_Initialize()
    Set p_Shell = CreateObject("WScript.Shell")
    p_Exec = Empty
    p_Available = "True"
    EC_Retrieved = ""
  End Sub
 
  Public Sub Open(cmd)
    Set p_Exec = p_Shell.Exec(cmd)
    PID = p_Exec.ProcessID
    p_Available = "False"
    p_ExitCode = ""
    EC_Retrieved = "False"
  End Sub
 
  Public Property Let Name(tempname)
    p_Name = tempname
  End Property
 
  Public Property Get Name
    Name = p_name
  End Property
 
  Public Property Get Available
    If Not IsEmpty(p_Exec) Then
      If p_Exec.Status <> 0 Then
        p_Available = "True"
        p_ExitCode = p_Exec.ExitCode
      End If
    End If
    Available = p_Available
  End Property
 
  Public Property Get ExitCode
    ExitCode = p_ExitCode
    EC_Retrieved = "True"
  End Property
End Class
 
The class has the constructor, one method, and 5 properties.  Next we look at how I used them.
 
First we set up a sample array of computers to test against.  This can be easily read in from Active Directory, a text file, or database table.  Then we create an array to store new instances of our RunCmd class.  In VBScript we cannot use the For Each…Next statement to populate each array element with a new instance of our class, me must use the standard For…Next which requires us to specify the number of items in our array.
 
Computers = Array("comp1","comp2","comp3","comp4","comp5","comp6","comp7")
Dim RunningProcesses(50)
for i = 0 to UBound(RunningProcesses)
  Set RunningProcesses(i) = New RunCmd
Next
 
After the array of RunCmd objetcs is set up it’s time to move to the main loop where the work is started.  StartProcess is called, which verifies the RunCmd object stored in that array element is not currently running an active process and if it is not it starts a new process to connect to the current computer fom the Computers array.  Once the process is started we move to the CheckProcess function which checks the number of objects in the RunningProcesses array have active processes running.  For the example I have set the number to 3.  So once 3 active processes have been reached it will loop until less than 3 are running.  As the CheckProcesses it calls it will retrieve data from the objects that have data.  So far the only thing I am capturing os the ExitCode.  I have not attempted to capture the StdOut as normally VBScript will wait for the process to finish, and we are try to avoid that!
 
For Each computer in computers
  StartProcess(computer)
  Do While CheckProcesses() >= 3
    WScript.Sleep 500
  Loop
Next
 
Once we have looped througn all of the computer we want to connect to we could still have processes in play, so we continue to run CheckProcesses while until we have 0 processes running.
 
Do While CheckProcesses() > 0
Loop
 
Here is the code for the StartProcess subroutine.  We pass the name of the computer we are attempting to connect to then we pass a command to to the Open method of object p.  When the method Open runs it assigns the PID of the process we started to the PID property of the object.
 
Sub StartProcess(cname)
  For Each p in RunningProcesses
    If p.Available = "True" Then
      p.open("cmd /c sc \\" & cname & " query snare|find /i ""_name""")
      p.Name = cname
      WScript.StdOut.WriteLine "Process Started: " & p.pid
      Exit For
    End iF
  Next
End Sub
 
The CheckProcess function is where we determine how many processes are running and grab the exit codes of completed processes.  When we get the Available property it check to see if the p_Exec object inside of our RunCmd object has a status other than 0, which means it has completed.  If it has not completed it increments the ProcessCount variable.  If it has completed it checks the EC_Retrieved property to see if we have already retrieved the ExitCode of the process.  If we have not retrieve it yet access the ExitCode property.  When we request this property it turns the EC_Retrieved property flag to True.  You can then code for whatever the expected error code might be.
 
Function CheckProcesses
  ProcessCount = 0
  For Each p in RunningProcesses
    If p.Available = "False" Then
      ProcessCount = ProcessCount + 1
    Else
      If p.EC_Retrieved = "False" Then
        If p.ExitCode = "1" Then
          WScript.StdOut.WriteLine p.Name & " – Snare is not installed."
        ElseIf p.ExitCode = "0" Then
          WScript.StdOut.WriteLine p.Name & " – Snare is installed."
        End If
      End If
    End If
  Next
  CheckProcesses = ProcessCount
End Function

 

There’s a lot of application for this code, and it’s a fun tutorial (I think) for creating your own classes in VBScript. Making your own class is a powerful tool and it can be used in tons of different applications.  Feel free to post a comment if you have questions or you’d like more examples!

EDIT: I have posted the complete code in the VBScripts download section!

Looking into the MAPI properties of a message object

Recently I had a reason to look into the available MAPI properties of a message object.   Microsoft pointed me to MFCMAPI, a tool available for free at codeplex.com.  The tool allows you to walk through your email profile, drilling down through all sorts of intersting objects.  The screenshot below shows my Inbox, and the specific properties of a message.  Notice cool flags like PR_REPLY_REQUESTED.  Unfortunately the property I was hoping to find was a timestamp for when the message was first opened and it doesn’t exist yet.  I did learn however that Exchange 2007 SP2 adds a message auditing feature that allows you to audit such actions as message opened.  Read more about Exchange auditing here.

Download MFCMAPI here.

Fun VBScript tip

When writing a VBScript designed to run at a command prompt (or "console") you some times want to not have to write data to a new line.
 
For instance, you might want to write a "." each time you loop through a array or collection so that you know something is happening.  Using WScript.Echo each use generates a CR\LF, much like using the vbCrLf constant or the textstream.writeline method.
 
This is where the WScript.StdOut.Write method comes in handy.  While not well documented this method works exactly like we need it to.  Take the following example where rsNames is a result set from an ADO query:
 
 
 
Do While Not rsNames.EOF
  If rsNames.Fields.Item(0).Value = "Bob" Then
    ‘ Write a period to show work then start a new line
    WScript.StdOut.WriteLine "."
    Wscript.StdOut.WriteLine "I found Bob!"
    Exit Do
  Else
    ‘ Write a period to show work
    Wscript.StdOut.Write "."
  End If
Loop
 
And if you really want to have some fun you could go for the "rotating line":
 
a = 0
b = 10000000
c = "\"
do while a < b
  if c = "\" then
    c = "|"
  elseif c = "|" then
    c = "/"
  elseif c = "/" then
    c = "-"
  elseif c = "-" then
    c = "\"
  end if
  wscript.stdout.write chr(08) & c
  wscript.sleep(100)
loop
 
Just for clarification chr(08) is a backspace and the sleep for 100 milliseconds makes the rotation direction of the line more obvious. : )
 
Have fun!