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.

Advertisements

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!