Ying Li at myITforum.com

PowerShell & System Center
MDT 2010 Deploy Windows 7 – Join the computer to the domain

I am using MDT 2010 to deploy Windows 7 image and I had some issues to make the computer automatically join the domain after the deployment!

By default MDT has this covered and you just need to answer a few question on this page


it will add the computer to the default “computer” OU, if you want to put everywhere else, just create a “DomainOUList.xml” file and save it in control folder, you will see a drop down error next to “option” for you to select the path.

The DomainOUList.xml looks like below and you could add more lines to it.

<?xml version="1.0" encoding="utf-8"?>
<DomainOU>OU=OU=Workstation,Windows 7,DC=xyz,DC=com</DomainOU>

This should work without issue. But in our environment we have a GPO to redirect the Windows Update to internal WSUS and also deploy ConfigMgr Agent which breaks my MDT deployment process because I enabled post-application Windows update which was redirected to the internal WSUS and start the ConfigMgr Agent install loop, eventually fails.

This blog talks about the workaround for scenario like this, I followed the instruction -

1) Removed the reference to domain join in Unattend.xml;

2) Put all the variables in customersetting.ini;

3) Added a TS to the end of the deployment;


I later realized that I have to move the “rename local admin” TS to the after domain join TS!

I gave it a try and my deployment now loops in “Recover From Domain”, which I go ahead to Disable “Recover From Domain” TS.

By now the domain join part works for default “Computer” OU but I still have trouble to add the computer to the OU I wanted, with some help from the MDT Forum and myself, I realized I got the OU path wrong!

With the syntax below, it is now much happier!

MachineObjectOU=OU=Desktops,OU=Windows 7,OU=Workstations,DC=xyz,DC=com
MachineObjectOU=OU=Laptops,OU=Windows 7,OU=Workstations,DC=xyz,DC=com

The lesson learned here is that there are a lot of good help (blog, forum etc) on the internet, they point you to the right direction but you still need to do some hard work yourself to work out the solution for your environment!

PowerShell Script to Get (Set) Logon Scripts for users of a certain group

I am currently leading an effort to Upgrade our client OS from XP to Windows 7. We decided to move away from logon script (mostly used for drive mapping & printer mapping) to Group Policy Preference.

So how we determine what logon script assigned to a particular user?

My friend Jeffery Hicks has a blog about this, I like the word “Legacy”, I have long been advocating to get rid of logon script using every opportunity I have!

PS S:\> get-qaduser -LogonScript * | sort LogonScript | select name, LogonScript

What if I want to get logon script information for users from a OU, no problem-

Get-QADUser -searchroot 'OU=whatever,DC=xyz,DC=com'|select name, logonscript

Now this always get me, what if I want to pull logon script information for users from a group? It’s a little tricky, as you can’t figure it out by simply do a get-help get-qaduser –full

After few trial and errors

PS C:\Users\yl.admin\Documents\PS> $grp = get-qadgroup 'CN=whatever1, OU=whatever2,OU=whatever3,DC=xyz,DC=com'

PS C:\Users\yl.admin\Documents\PS> get-qaduser -MemberOf $grp|select name, logonscript

Last but not least, the whole point is not just get the logon script assigned to users but to get rid of them!

PS C:\Users\yl.admin\Documents\PS> get-qaduser -MemberOf $grp|set-qaduser –logonscript “”

When you P2V a domain controller – be mindful with the “hidden” physical NIC!

We suddenly started to get the below OpsMgr alerts as on some servers we monitor event viewer.

Alert:  System Event Monitoring Rule
Source: whatever.xyz.com
Last modified by: System
Last modified time: 5/18/2010 12:09:53 PM
Alert description: Event Description: The browser service was unable to retrieve a list of servers from the browser master \\DC1 on the network \Device\NetBT_Tcpip_{E85097A4-D2FE-4E45-A53E-D47CD0F2DB1B}.
Browser master: \\DC1
Network: \Device\NetBT_Tcpip_{E85097A4-D2FE-4E45-A53E-D47CD0F2DB1B}
This event may be caused by a temporary loss of network connectivity. If this message appears again, verify that the server is still connected to the network. The return code is in the Data text box.
Alert view link: "http://MOM01:51908/default.aspx?DisplayMode=Pivot&AlertID=%7b6adff6b8-dd97-4a5b-bee4-6223ce0c593b%7d"
Notification subscription ID generating this message: {13442928-EA9E-7BF7-90C6-0F7AD57A0C94}

We followed this article and checked the File and Printer Sharing for Microsoft Networks which is enabled on DC1. I then found this article and the below session got me thinking!

“This error occurred on one of our DHCP/WINS/DNS servers (Win2k) due to replacement of hardware (NIC card). After the replacement, Windows recognized a hidden NIC with the same IP configuration. The old NIC was not visible when showing hidden devices in device manager. After removing the old card by using devcon.exe (see M269155), the message disappeared. “

That is not the exact case for us but I do remember DC1 recently upgraded to Win2K3 SP2 and was P2Ved afterword, which will have the same “hidden physical NIC” problem!

I then follow this thread removed the “hidden” NIC card, reboot DC1. The Error/Alert is GONE!

MDT 2010 – Create a Task Sequence using PowerShell to Rename Local Administrator Account

I am working on to create a Windows 7 image using MDT 2010, one of the request is to rename the local administrator account in the image. I did this in XP build which I have no problem, rename the local admin account then capture the image. The deployed image will keep my renamed local admin account. But in Windows 7 image, I just can’t do that! I rename the admin account, capture the image. But when I deploy the captured image, the local admin account reverse back to “administrator”. The indication is this is by design, may have something to do with UAC?

Here is my solution, I built my “gold” image without trying to rename local admin account. I then deploy the captured image but add a task sequence to rename local admin account. There are some ways to do this, but with my PowerShell root, I decide to accomplish using PowerShell! Michael Niehaus has a blog about how to create a task sequence using PowerShell. All we need to do is to save our PowerShell script in the %ScriptRoot% folder and then reference it in the Task Sequence like

PowerShell.exe – File “%ScriptToot%\RenameLocalAdmin.ps1

But there is a catch, as we know by default, PowerShell set the execution policy to “restricted”, in order to run our PowerShell script, we need to change the execution policy to at least “Remotesigned”


Here is the exact command enlightened by this thread

Powershell.exe -command "Set-ExecutionPolicy RemoteSigned; cpi z:\scripts\RenameLocalAdmin.ps1 -Destination c:\; c:\RenameLocalAdmin.ps1; ri c:\RenameLocalAdmin.ps1; Set-ExecutionPolicy Restricted"

What it does is to set the ExecutionPolicy to “RemoteSigned” ;

Copy our script from %ScriptRoot% locally to the C drive;

Run the script; and delete the script from C drive;

Last but not least, set the ExecutionPolicy  back to “Restricted”

Here is what’s in my RenameLocalAdmin.ps1


So now with this technique, running PowerShell Task Sequence in MDT 2010 is as easy as 1 – 2 – 3! :)

Windows PowerShell Cmdlets - Bing


Windows PowerShell Cmdlets – Bing


Just do a PowerShell search using Bing – it’s very cool!

OpsMgr – Create a group for SAN attached servers

I need to monitor system event 100 – EMC PowerPath generated error. In my previous blog, I detailed how to setup a monitor/rule to monitor event. The challenge here is that I want to target my monitor/rule to servers attached to EMC SAN. Here is how I did it -

First, I need to create a new WMI based custom attribute in OpsMgr.

In OpsMgr console – click Authoring, expand Management Pack Objects, right click Attributes, choose Create a New Attribute



Click Next,


Next to Discovery Type, choose WMI Query, Target Windows Computer, OpsMgr will automatically change the Target to Windows Computer_Extended and I changed that to Windows Computer_SanAttached. Then pick an unsealed Management Pack, then click Next


In this screen, I specify the WMI Namespace is root\cimv2 and the WMI query is

Select * From Win32_DiskDrive Where Model=”PowerDevice by PowerPath”

Property Name is Model and the query interval I set it to 3600 second as we don’t pop up a SAN attached server that often!

Click Finish – I am done creating the new custom attribute.

Next, I am going to create a new group based on this custom attribute.

The Dynamic Rule looks like this – The class is Windows Computer_SANAttached, select your custom attribute SAN Attached, the operator is “contains” and the value is “PowerDevice by PowerPath”


Last but not least, override my monitor/rule using the group I just created!

you could use this as a guide to create your custom attributes then groups at will!

Enable PowerShell Remoting on Windows 7 (RC) Machine is THAT EASY!

I have a Vista SP 2 box and Windows 7 (RC) box. On the Vista box, I have PowerShell V2 installed  and PowerShell remoting enabled a while back and I even forgot the exact cmdlet I used.

This is in a AD environment and I have domain admin right on both machines. So from Windows 7 to Vista box, I could do this

invoke-command –computername pluto {get-process}

After a short but noticeable wait as PowerShell does need to establish a remote session(PSSession), to execute the get-process cmdlet on the remote computer PLUTO and get the output back.


but when I tried to do the same thing from Vista to Windows 7, I get errors.


Which does make sense because this windows 7 machine is relatively new and I haven’t do anything out of the box related to PowerShell. So I followed the instruction


Now I am back on the Vista machine and run the cmdlet again, Whola, now I see the PowerShell Remoting is in Action in Real Time (On Windows 7 that is)!


So as you can see, PowerShell team really make this process rather easy and nothing to feel intimidated. You can get creative from here, try this on multiple machine and instead running get-process, you can run any script block you have remotely just put them in {}.

PowerShell Remoting and Sessions

As we all know that Remoting is a big thing in PowerShell V2, you may not know that the key thing to PowerShell Remoting is the “Sessions”. Session is actually not new at all to PowerShell, it exist in V1 and it doesn’t have to be remote!

Let’s see the below cmdlet

get-service i*

It is running locally but it is a (hidden)session

It is equivalent to the below cmdlet

Invoke-command {get-service i*}

To extend that to a remote computer “whatever”, you do this -

Invoke-command –comutername whatever –command {get-service i*}

What happens here is invoke-command establish a session to remote computer “whatever” and then run the cmdlet get-service on the remote computer and then terminate the session.

Not only you could run cmdlet after invoke-command(established a session), you could run a block of scripts as well.

Now you also can do this -

$sessions= new-PSSession –computername whatever1, whatever2

Basically what it does is establish new (remote)sessions on the remote computer whatever1/2 and make them available to you, so you could run your scripts.

Invoke-command –session $sessions {get-eventlog –log security} |sort *computername*

So the point I am trying to make here is that in PowerShell V2, you use invoke-command cmdlet to connect to multiple remote computers and establish “sessions’ to the remote computer and then you run your cmdlets or scripts blocks. Here are some of the cmdlets related to session – New-PSSession; Get-PSSession; Remove-PSSession. You can get the session related cmdlets by using get-command *-PSSession and then you can use get-help to learn the details.

PowerShell (V2) remoting

As we already know that Windows 7 will become generally available on Oct. 22, 2009, and Windows Server 2008 R2 will be broadly available at the same time.

For PowerShell funs, PowerShell V2 will be integrated with Windows 7 and Windows 2008 R2! That is, you don’t have to install (XP/Windows 2003) or enable the feature (Vista/Windows Server 2008). It’s installed and enabled for you!

I have been using PowerShell CTP2/3 and running Windows 7 RC recently, therefore effective using PowerShell V2.

PowerShell V1 has very limited remoting capability. For example, you can’t do get-service on remote computer.  In order to get remote service status, I have to use these codes -

$HealthService = [System.ServiceProcess.ServiceController]::GetServices($Computername) | where{$_.name -eq 'HealthService'}

Now with PowerShell V2, I modified the above codes -

$HealthService = Get-Service -Name "Healthservice" -ComputerName $Computername

It’s much cleaner and Admin friendly! ;)

PowerShell remoting is built on top of Windows Remote Management (WinRM), which is Microsoft’s implementation of WS-Management protocol, which also integrates with WMI.  WinRM enables you to run WMI scripts against remote computers by using standard Internet protocols like HTTP and HTTPS, that means you can manage remote machines over the Internet.

In order to take full advantage of the V2 remoting feature, you do need to install PowerShell and WinRM on the target machines which I know it’s kind stretch in mostly Win2K3/XP Production Environment. But on Vista/Windows server 2008, they are features you could enable, with the upcoming release of Windows 7 and Windows 2008 R2, they are on by default. So it’s about time to get your hands dirty with PowerShell V2/WinRM!

How to setup a Configmgr hierarchies between two AD Forests without trust

Here is a document for setting up Configuration Manager in multiple Active Directory Forest with or without trust. It kind touch on everything but lacking the detailed instruction as to how to set it up!

Let’s say you have a production AD forest which have Configmgr hierarchies in place, you have another AD forest setup for commercial facing websites or whatever reason. The security requirement is NO TRUST between the two forests. How are you going to setup the site to site communication between these two forests?

The forest/domain functional level are Windows server 2003 and above (which meet the minimum requirement – Windows server 2003 domain/forest functional level! Both site has to be primary site according to the doc above! They are both in Mixed mode in our case.

1) Create a standard sender address in both forest in a domain\user format as the computer account won’t work in this scenario;


2) Add the site address account you created above to the Site to Site connection group on the respective site server';


3) Set the Central site (in production forest) as the Parent site for the new site;


4) Last but not least – Manually exchange the public key follow the below link!


That’s all you will have to do to setup site to site communication cross active directory WITHOUT TRUST!

The wait is over – OpsMgr 2007 R2 hits RTM

Yesterday, the OpsMgr 2007 R2 finally hits RTM, you can download the evaluation copy here. The production version will be available until July 1st. But you can install the evaluation copy now and upgrade later.

OpsMgr 2007 SP1 – Creating a rule to monitor logon event

At times some people use service account to logon to machines and it has the potential to lockout the service account and therefore bring down the service which is not acceptable to the business and we would need to create a monitor or rule to monitor this behavior.

We can create a monitor or rule, the process is pretty much the same except the monitor will affect the availability of the business application and the rules do not. Another thing need to pay attention is how you reset the monitor once the state change occurred. So I prefer to use the rule instead:

Go to authoring console, expand Management Pack Objects, right click the rule and select create a new rule, in the next window, expand Alert Generating Rules, expand Event Based, select NT Event Log (Alert), make sure save it in a different Management Pack other than Default Management Pack, see below


Type a name for your new rule, set the Rule Category to Alert and set the Rule target to Windows Computer and uncheck Rule is enabled check box. We will do a override later to enable this rule for a targeted group. It is kind counter intuitive but that’s how this things works!


Choose Log name to “Security”


For the purpose of this blog, I am targeting a Windows 2008 domain and in windows 2008 domain the event ID for successfully logon event is 4624. If you are targeting Windows 2003 domain the event ID is different!

Delete the Event Source row and click Insert, choose the third option – Use parameter name not specified above and type EventDescription in the box and click OK.


In the next window, set the Operator to "Contains” and Type the target account in the Value column (xyz\yli here)


In the next screen, Type $Data\EventDescription$ in the alert Description Field.


Click on the Create button to create the rule.

Next do a overwrite to enable this rule for a pre-defined group which contains all the Windows 2008 domain controllers since all the logon event will be logged on the domain controllers.


That is all you need to create a monitor/rule to monitor account logon event. There are some other post outs there but the procedure above did worked for me!

Posted: May 18 2009, 12:55 AM by yli628 | with 1 comment(s)
Filed under:
Antivirus Software Hijacked my Virtual Machines!

It cost me a few hours the other day to do a mini – DR for one of my Hyper-V host! Hopefully this post will save you some trouble in the future.

The trouble starts when I try to build a new VM through VMM 2008, I got the error like below -

The requested operation cannot be performed on a file with a user-mapped section open. (0x800704C8)

Then I thought what about try to create the new VM using Hyper-V manager, I got this error -

VMName’ Microsoft Synthetic Ethernet Port (Instance ID
{7E0DA81A-A7B4-4DFD-869F-37002C36D816}): Failed to Power On with Error 'The specified network resource or device is no longer available.' (0x80070037).


I decided to reboot the Hyper-V hosts (this is off business hours and none of my Hyper-V guests are mission critical), and to my unpleasant surprise, four of my six VMs are gone! I would love to see if all the VMs are gone, then it could be the SAN (all the VMs are on a SAN disk) issue. But it puzzles me that just four of them are gone. I checked the SAN disk and it is visible to the OS and I checked all the VHD files for my VM are still on the drive which is encouraging – I could recover the VMs one by one.

But what could cause this problem, I couldn’t create new VM at first and then I lost VMs after reboot the host. I started to call MS support and at the mean time search the internet myself. The search point to Forefront, TrendMicro(which we use), as a matter of fact, all the antivirus software as the culprit.

Now I remember that just recently one of my coworker installed TrendMicro client on the Hyper-V host. So I disabled TrendMicro client on the Hyper-V host, indeed I could create new VMs after that and I was able to restore the “lost” VMs. Later MS tech call me back and confirmed that the antivirus (TrendMicro in our case) software can corrupt the VM configuration files and therefore lost the VMs after rebooting the host. It can also prevent us to create new VMs.


Lesson learned, we need to create exclusion for our Antivirus software for all the Hyper-V host following the above KB!

PowerShell 1.0 is now available as an optional update

Great News! On Tuesday (3/24/09),Microsoft published PowerShell 1.0 on Windows Update!

PowerShell 1.0 through MU is only offered:

· As an optional update

· If you don’t have any other version of PowerShell installed (including V2 CTPs) on your machine

· .NET 2.0 is installed on the machine

· OS is one of: XP-SP3, W2K3-SP2, Vista-RTM, Vista-SP1

With PowerShell 1.0 on Windows update, it will now reach a  broader audience, (3.5 million downloads so far). This will help the user community to get ready for the rich set of features of PowerShell V 2.0 on the upcoming release of Windows 7 OS. Also it is safe to assume that the PowerShell 2.0 will be pushed through WU/MU/WSUS for enterprise deployment.


PowerShell Script to Create Multiple Exchange Mailbox and Set Primary SMTP Addresses

I am working on a request to create ~800 user's Exchange 2007 SP1 mailboxes from an excel file.

The file with the following headers filled with user's information.

Name, FirstName, MI, LastName, Email, OU

The Alias will be in firstname.mi.lastname if there is a MI or otherwise in firstname.lastname format.

I come up the following script:

$Password=Read-Host "Enter Password" -AsSecureString

Import-CSV c:\users.csv | foreach {new-ma
ilbox -firstname $_.firstname -initials $_.MI -Lastname $_.lastname -alias ($_.f
irstname + "." + $_.MI + "." + $_.lastname) -name $_.name -userPrincipalName $_.
email -database "server1\storage group" -org $_.OU -Password $Password -Reset
PasswordOnNextLogon $True}

If the MI column is blank, just removed $_.MI part from the script because Exchange doesn't understand the firstname..lastname!

I thought that would be it, right? Not quite!

It turns out we have a default policy in place which will automatically change the email address format to first initial + lastname (yli instead ying.li)

So I modified the script as follows:

Import-CSV c:\users.csv | foreach {new-ma
ilbox -firstname $_.firstname -initials $_.MI -Lastname $_.lastname -alias ($_.f
irstname + "." + $_.MI + "." + $_.lastname) -name $_.name -userPrincipalName $_.
email -database "server1\storage group" -org $_.OU -Password $Password -Reset
PasswordOnNextLogon $True}|set-mailbox -emailaddresspolicyenabled $false

which will check off the "Automatically update e-mail addresses based on e-mail address policy" box in user's mailbox property (email address page), this will prevent the newly created user from inherit the default policy.

I checked the results - the email address already re-formated to yli instead ying.li before the check off policy taking place!

I end up have to run the below script against the excel sheet to reset the user's primary smtp address to ying.li@whatever.com

Import-CSV c:\users.csv | foreach {$user
= get-mailbox -id ($_.firstname + "." + $_.MI + "." + $_.lastname);$user.emailad
dresses.add($_.email);set-mailbox -instance $user -primarysmtpaddress $_.email}

Now it is all happy!

More Posts Next page »