Powershell 5 in Windows Management Framework V5 Preview

Microsoft released a Preview of the Windows Management Framework V5. As in the past, this package ships with the according version of Powershell. Powershell V5 will bring interesting new Features.

Among those are:

  1. OneGet module with a set of comandlets to manage Software packages
  2. Commandlets to manage L2 Layer Network Switches

Find the introduction article for Windows Managagement Framework V5 by Jeffrey Snover here on TechNet.

You can dowload the Preview here.

However, the mixture of Powershell Versions we find at customer Environments will get wider and wider. Same is valid for modules like ActiveDirectory or SnapIns for Exchange. One will need to start with a lot of checks in the beginning of the code when a script is planned to be used universally.

How to write or migrate sidHistory with Powershell (3)

In our large scale Active Directory Cross Forest migration project, we now have migrated already 40.000 user accounts globally. Our self made scripting routine to migrate/write sidHistory into the target accounts turned out to be a robust, reliable part of the process and I feel safe now to share some experiences. We are running it on multiple migration servers around the globe as scheduled task – which you can easily call a “service” as it is running every 5 minutes.
I will write about the whole mechanism of how we automated our large scale Active Directory migration in another blog post, but will concentrate here to share our way of managing the sidHistory part.
As you know already from part 2 of this blog post, we were buidling our code on the examples that MSFT Jiri Formacek published here.

However, 2 main restrictions prevented us from using this code as is:

  1. We wanted to make sure that we really used the Domain Controller with the PDC Emulator role from source domain. Our source environment has 100+ domain controllers and the PDC role is siwtched from one DC to another DC under certain conditions. Therefore to use a fixed name for the PDC role Domain Controller was not acceptable.
  2. Our Active Directory account migration process was fully automated and it was the user who starts his/her migration not us. Therefore the requirement was given, that we only can run sidHistory migration (together with the account activation in target domain) as a continuous background service. Every session based approach would not have helped like we can find it in ADMT or Dell Migration Manager for Active Directory.
    Prepopulating sidHistory on the previously created disabled accounts in target domain was not an option, since Exchange 2010 was giving errors for disabled users with sidHistory of source active users under certain circumstances.

Solutions:
1) This was not a big thing. A small function could do the trick.

function getPDCE($domain) {
$context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("domain",$domain)
$PDC=[System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($context).PdcRoleOwner.Name
return $PDC
} 

2) This was not that easy (for us). Running our account migration script as usual – means as scheduled task with admin credentials – did not work for the sidHistory part in it since the credentials of the logged user account were not handed over to the SIDCloner routine.
All the code we could find on Jiri’s page asked for credential information interactively or would need explicit credentials in the script in another way.
Although we are packaging our Powershell Scripts into an .exe file by using Sapien Powershell Studio and could hide the password from simple file editing, putting user name and password into the script was not an acceptable way for us to go.
After testing back and forth, someone cam up with the idea of using the Windows credential manager to work around our deadlock situation.
The script would access the credential manager interface, get the credential information from there and would then pass them to the DsAddSidHistory function.
We created a function to retrieve credentials from Credential Manager store based on a very good script example to be found on Technet here.
While this seemed to be a clever way of achieving our target of having a scheduled user account activation script with sidHistory functionality, we ran into errors again. Retrieving credentials from Credential Manager by script obviously fails, when the script runs with exactly the credentials that you want to retrieve. This was true in our case, because the user account migration script was scheduled with that “big admin” account.

The solution finally was:
The user account migration script was running as a scheduled task with full admin credentials. When it came to migrate (in our project setup: activate) a user account in the target domain, it did not (could not) write sidHistory, but created an input file with username and target DC (the DC closest to the site where the user was had logged in from – remember that the user triggers his/her migration in our project).
On the same migration server a second script was scheduled with a server-local admin account. This script consists of 3 parts. First part is to check if there are new input files. Second part is to retrieve the full admin credentials from Credential manager and passing them to second part. Third part is to migrate sidHistory which succeeds because you have put all parts together for the SIDCloner routine:
PDC Emulator DC for source you have found by query.
Target DC was in file (but you can take every writable you want if replication delay does not matter).
Explicit credentials you get from Credential Manager.
Nowhere in both scripts password information is saved in clear text.

Additional Information

How to write or migrate sidHistory with Powershell (2)

SIDCloner.dll
When you look in WWW to find a way to write sidHistory attribute, you probably will stop at the marvelous SID Cloner website created by Jiri Formacek, MSFT (http://code.msdn.microsoft.com/windowsdesktop/SIDCloner-add-sIDHistory-831ae24b). Jiri provides a managed class library that implements the SIDCloner class, which we can use in Powershell and any .NET programming code.
The SID Cloner class is built upon native API to migrate sidHistory and therefore uses the DsAddSidHistory function under the hood (http://msdn.microsoft.com/en-us/library/windows/desktop/ms675918(v=vs.85).aspx).
Although we do not need to install ADMT on any machine to run the SID Cloner code, we still have to consider to meet the same requirements for the migration setup as ADMT does. SID Cloner and ADMT come from the same “mothership” DsAddSidHistory.

SidHistory requirements
In brief we need the following prerequisites to be in place before we can start writing sidHistory (http://msdn.microsoft.com/en-us/library/windows/desktop/ms677982(v=vs.85).aspx):

+ a trust relationship must exist between source and target domain
+ source and target domain must not be in the same Active Directory forest
+ for source domain all actions will have the PDC Emulator as target; you cannot bind to another DC than the one with the PDC Emulator role
+ Auditing must be enabled in source domain
+ a domain-local group “domain$$$” must be created in source domain
+ a special registry key must be created on PDC Emulator DC in source domain: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\TcpipClientSupport
+ Audit Mode must be turned on in each domain and also Account Management auditing of Success/Failure events must turned to on.
+ the Security Identifier we want to transfer must not already exist in any sidHstory attributes of objects in target domain

Permissions:
For running Powershell code based on SID Cloner you do not necessarily need domain admin credentials in target domain. While read permissions on objects in source domain are sufficient (you are reading the “standard” attribute objectSID there), the permissions to modify the object in target domain by writing the sidHistory value requires more:
+ full access permissions to the object (better OU)
+ special permission “migrateSIDHistory” on the Active Directory domain object in target domain
migrateSidHistoryPermission

With all the requirements settled, you are able to migrate sidHistory by using the sample script, that Jiri published on the SID Cloner Website.

However, the most easy way to use the 4 fold overload with SIDCloner did never work in our tests.
Overload with 4 arguments means, you simply define source and target domain, source account from which you want to take the objectSID and target account where you want to write sidHistory.
This approach will probably never succeed because
a) the strict PDC Emulator access is not guaranteed when using a domain call
b) the credentials of the interactively logged user are obviously not passed to the DsAddSidHistory function inside the SID Cloner dll

Let’s have more findings around this Topics in Part 3 of this blog post.

 

How to write (migrate) sidHistory with Powershell (1)

The adventure of sidHistory
I spent quite some hours during the last weeks to create a Powershell script routine that is able to “migrate sidHistory”. Migrate sidHistory in this context means to read the objectSID of a given user or group source object in Active Directory Forest A and write this value into the sidHistory attribute of a selected user or group object in Forest B.
Assuming there is a trust relationship between Forest A and Forest B and sidFiltering is disabled, user B from Forest B who has the sidHistory attribute filled with the SID of the user A from Forest A will have access to the same resources in Forest A like user A himself. The reason for this behavioris found in the fact that the security token of User B after successful logon will contain the SID of user A. From Windows’ token based access strategy, user B is now a user AB as long as we are talking about SID releated resource access.
From those few lines everyone agrees on the statememt that sidHistory functionality can be abused to get access to resources which are restricted for a user by default. In principle, you can add the SID of a given user from Forest A to any user in Forest B. There does not have to be a dedicated relationship like identical samaccountname of the two users (groups) etc. While exactly his functionality helps to ease Inter-Forest Active Directory migrations (and Intra Forest Migrations as well when you take care), it can also be a dangerous thread against your Active Directory security.
However, this not a new finding and Microsoft did well in treating sidHistory as a special attribute. It needs special treatment when you try to clear the values and it needs special treatment when you want to write values into it. I already published 2 posts about deleting sidHistory, see [Link], so we will concentrate here on writing sidHistory.

Writing sidHistory
The most common way in today’s Active Directory migration scenarios is writing sidHistory by using a migration software. Microsoft ships its own migration software called Active Directory Migration Tool (ADMT) which is capable of writing sidHistory. Other vendors like Dell Software’s Migration Manager for Active Directory (formerly known as Quest Migration Manager for Active Directory) provide a similiar functionality with a lot more options and the possibility to write sidHistory in an ongoing Active Directory Synchronization. Up to now Microsoft Forefront Identity Manager cannot help us here out of the box to fill this attribute as part of an Active Directory synchronization.
When you try to put a SID into the sidHistory attribute by using the standard Microsoft administrative tools like the attribute editor from ADUC, you will fail for sure.

errorsidhistory

You will also fail by using Powershell integrated LDAP based write operations for this attribute like set-aduser or set-qaduser.

errorsidHistory_a

We have to dig deeper here to reach our goal of Powershell based writing of sidHistory, which we will do in Part 2 of this blog post.

Moving servers between domains with Powershell v3 add-computer commandlet

Background
Migrating computers as part of an Active Directory migration has 2 aspects. There is an Active Directory object migration as it is with user and group objects. And in addition you have to disjoin the Windows computer from the old domain and join it to the new domain which requires to modify the workstation or server OS.
The most simple way to move a computer between domains is of course to use the GUI and change the domain or workgroup association of the computer in the system property settings manually.
However, this is not a solution for migration projects where you want to move many computers at the same time remotely logging on to the machines interactively.

Requirements
In our actual large scale migration project we have to deal with multiple thousands servers that have to be migrated to the new domain. While the client computers run through an SCCM controlled imaging process, the server domain move is the task of the server admins. Some of the server admins are responsible for large scale test and development environments with hundreds of servers.
To ease the one time task of domain migration for those administrators, our idea was to implement a remote service utility which can migrate servers to the new domain in bulk mode.
While we would operate and maintain the service centrally, the server admins should decide which servers to migrate and when to migrate them. Another requirement was to leave the servers as is without installing QMM agents that could interfere with running applications.

server move automat


Solution

To be as most flexible as can be, we chose the add-computer Powershell commandlet for our scripting solution. (We ended up with a 320 line script to combine multiple modifications during server move). Server owners would place a config file on a share. The script server periodically scans the share for new config files and processes the server names.
While the final script contains multiple functions, the core function with the add-computer commandlet to disjoin/join the Computer can be found here:

function domain_move($compacc,$fqdn) {
$username_joinTarget=”TARGETDOMAIN\SERVICEACCOUNT”
$password_joinTarget=cat“d:\scripts\server_move\JoinTarget.txt”|convertto-securestring
$cred_JoinTarget=new-object -typename System.Management.Automation.PSCredential –argumentlist $username_joinTarget,$password_joinTarget
$username_unjoinSource=”SOURCEDOMAIN\SERVICEACCOUNT”
$password_unjoinSource=cat“d:\scripts\server_move\UnjoinSource.txt”|convertto-securestring
$cred_UnjoinSource=new-object -typename System.Management.Automation.PSCredential -argumentlist $username_unjoinSource,$password_unjoinSource
$Error.clear
Try {Add-Computer -ComputerName $compacc -DomainName $TARGETDOMAIN -Credential $cred_JoinTarget -UnjoinDomainCredential $cred_UnJoinSource -Server $TargetDC -PassThru -Verbose}
Catch {return $false}
Start-Sleep -Seconds 10
Restart-Computer -ComputerName $fqdn
return $true}

The variables $compacc and $fqdn come from the main part of the script as parameters when calling the function.
$compacc=”samaccountname of computer to migrate”
$fqdn=”full qualified domain name of computer to migrate”
The text files with the encrypted passwords are located in the same directory as the executable or ps1 script.

Discussion
The add-computer commandlet was introduced with Powershell 2, but had the restriction that you could only migrate the local computer and needed to run Powershell Remoting to make it useful for other computers.
With Powershell version 3 the parameter –computer was added which allows you to address remote computers for domain move.
Note: This parameter does not rely on Powershell Remoting
Another important parameter for us is –server which defines the target DC that will control the domain join operation. Since we are creating the computer object in the target domain in a specific OU in advance in the same script, it is important not to run into replication delays (trying to join while the computer object was not replicated). The –server parameter which never worked properly in Powershell version 2, did its job for us as long as we used the FQDN of the Domain Controller as syntax.
Note: If you cannot succeed with the domain\DC value for the –server parameter as listed in the Tech Net article, try out the FQDN instead.
A remarkable caveat of the add-computer commandlet for server domain move is the explicit input of domain credentials for disjoin and join actions. Even when the account that is running the script or scheduled task keeps all necessary permissions, you still have to pass account and credentials to make the domain join working. We suppose that this is a WMI restriction and that WMI is underlying code here. Check out the WMI commands below. To overcome this limitation we captured the encrypted passwords in 2 separate text files and only listed the service account in the script code. In the final version the script code was transformed into an exe file by using Powershell Admin Studio by Sapien Technlogies.
The add-computer commandlet also provides a parameter –restart. We cannot recommend to use this parameter, because it might trigger the reboot too fast, which can lead to RPC connection errors after reboot. We recommend to set a sleep time of multiple seconds and trigger a separate restart-computer commandlet which provides you with multiple options and restart dependencies.
We do not use the –path parameter but create the computer account in a separate function.
For a full amount of Parameters please check Tech Net.

Alternatives to the Add-Computer commandlet

Quest Resource Updating Manager
If you have deployed Migration Manager for Active Directory in your migration project, you can create collections for computers that should undergo a domain move. The collections can be filled by import scripts, so that you can achieve a semi-automatic solution.
QMM Resource Updating Manager
While the main purpose of QMM Resource Updating Manager is to prepare the resources (file shares, local groups, registry etc) for the domain move (which either requires to install agents or to deploy vmover scripts), it also has an option to move computers remotely without installing agents.
QMM Resource Updating Manager


NETDOM

Another option is the NETDOM JOIN legacy command which is around since Windows NT 4.
http://technet.microsoft.com/de-de/library/cc772217(v=ws.10).aspx
(To use netdom, you must run the netdom command from an elevated command prompt.)

WMI
Another way is to go WMI native and use the commands that might be underlying of the add-computer commandlet. However, we find WMI a bit “clumsy” for this purpose (we like it easy).
Example:
$currentserver= (gwmi -computername $Computer -class “Win32_ComputerSystem” -Authentication 6)
$currentserver.JoinDomainorWorkGroup($newdomain,$password,$username,$Null,33)