Thursday, July 24, 2014

SCCM: "An error occurred while retrieving policy for this computer (0x80072EE7)" in WinPE

The Error:
Error 0x80004005 right at the beginning of the process.

The smsts.log shows 

Other Symptoms:
Client gets a DNS IP like fec0:0:0:fff::1%1

Solution:
Check and if required set the DHCP option 6 to define the DNS server address.

Wednesday, July 16, 2014

HOW-TO Populate MDT Database Roles using a Powershell Script


I recently had to build additional MDT servers to deploy our OSes and one of the task I dreaded was adding the roles properties to the MDT database manually. 

Not only it takes time but it is also very error prone, i.e.: update the wrong field, misspel the admin password,....

After searching the internet I was able to put this script together and use it to add our roles on each server.

I hope you enjoy it and it helps you save some time.

Until next time ...

#Download MDTDB module from URL:
#http://blogs.technet.com/b/mniehaus/archive/2009/05/15/manipulating-the-microsoft-deployment-toolkit-database-using-powershell.aspx

########################### Variables initialization ###########################
#To import the MDT Database and MicrosoftDeploymentToolkit Powershell module
$MDTDB_Module_Path = "E:\MDTDB\MDTDB.psm1"
$MDT_Module_Path = "C:\Program Files\Microsoft Deployment Toolkit\Bin\MicrosoftDeploymentToolkit.psd1"

#Server name or IP to connect to the database
$Servername = "10.10.10.10"

#To set the name of the database where we want to add roles
$DatabaseName="MDTUATDatabase"

#Array to add applications to each role - Apps GUIDs are server specific.
$Apps=@('{435c616b-80a7-43ef-8a91-d1ea95ca2a89}','{999c616b-ab77-43ef-8a91-d1bd5ca2a10}')

####################################  Main ####################################
#Import the MDT Database Powershell module
import-Module $MDTDB_Module_Path

#Import MicrosoftDeploymentToolkit module
import-module $MDT_Module_Path

#Create a PSDrive to the MDT Deployment Share
New-PSDrive -Name (Get-MDTPersistentDrive).Name -PsProvider MDTProvider -Root (Get-MDTPersistentDrive).Path -Verbose

#Create a new Database ( optional)
#New-MDTDatabase -Path "DS001:" -SQLServer $Servername -instance "SQLEXPRESS" -database $DatabaseName -SQLShare "\\$Servername\DeploymentShare$"

#Connect to the database
Connect-MDTDatabase -sqlServer $Servername -instance "SQLEXPRESS" -database $DatabaseName

#Set role specific variables
#Role name that appears in the MDT Gui
$RoleName = 'Europe-Germany-UAT'

#Hash table with details tab entries for the above role
$Settings=@{
'OSInstall' = 'YES'
'JoinDomain' = 'MY.LAB.CORP'
'DomainAdmin' = 'MyDomainAdminAccount'
'DomainAdminDomain' = 'LAB'
'DomainAdminPassword' = 'xyz123456789'
'MachineObjectOU' = 'OU=Workstations,OU=Computer Accounts,DC=MY,DC=LAB,DC=CORP'
'TimeZoneName' = 'W. Europe Standard Time'
'KeyboardLocale' = '0407:00000407'
'InputLocale' = '0407:00000407'
'UserLocale' = 'de-DE'
'SystemLocale' = 'de-DE'
'UILanguage' = 'de-DE'
'SkipCapture' = 'yes'
'SkipAdminPassword' = 'yes'
'SkipApplications' = 'yes'
'SkipComputerBackup' = 'yes'
'SkipDomainMembership' = 'yes'
'SkipUserData' = 'yes'
'SkipLocaleSelection' = 'yes'
'SkipProductKey' = 'yes'
'SkipSummary' = 'yes'
'SkipBDDWelcome' = 'yes'
'SkipTimeZone' = 'yes'
'SkipBitLocker' = 'yes'
'SkipDiskPart' = 'yes'}

#Add the role with above params
New-MDTRole -name $RoleName -settings $Settings | Set-MDTRoleApplication -applications $Apps

#Repeat the process for each new role

$RoleName = 'Europe-Italy-UAT'
$Settings=@{
'OSInstall' = 'YES'
'JoinDomain' = 'MY.LAB.CORP'
'DomainAdmin' = 'MyDomainAdminAccount'
'DomainAdminDomain' = 'LAB'
'DomainAdminPassword' = 'xyz123456789'
'MachineObjectOU' = 'OU=Workstations,OU=Computer Accounts,DC=MY,DC=LAB,DC=CORP'
'TimeZoneName' = 'W. Europe Standard Time'
'KeyboardLocale' = '0410:00000410'
'InputLocale' = '0410:00000410'
'UserLocale' = 'it-IT'
'SystemLocale' = 'it-IT'
'UILanguage' = 'it-IT'
'SkipCapture' = 'yes'
'SkipAdminPassword' = 'yes'
'SkipApplications' = 'yes'
'SkipComputerBackup' = 'yes'
'SkipDomainMembership' = 'yes'
'SkipUserData' = 'yes'
'SkipLocaleSelection' = 'yes'
'SkipProductKey' = 'yes'
'SkipSummary' = 'yes'
'SkipBDDWelcome' = 'yes'
'SkipTimeZone' = 'yes'
'SkipBitLocker' = 'yes'
'SkipDiskPart' = 'yes'}

#Add the role with above params
New-MDTRole -name $RoleName -settings $Settings | Set-MDTRoleApplication -applications $Apps

Monday, June 23, 2014

FIX: "LTIApply: Path not found (76)" - building Hyper-V VM

I recently got an issue that I had fixed before but could not remember how ... better post it once and for all. 

When deploying an image on an Hyper-V VM with MDT 2012 Update 1 or MDT 2013, at the beginning of the process - in the WinPE stage - I got the "ZTI ERROR - Unhandled error returned by LTIApply: Path not found (76)"


After checking various logs - which are still located on the ramdisk at that stage of the sequence - I found in the Variable.dat  that the properties IsDesktop and IsVM where both set to True. This was also showing in the ZTIGather log.




Checking the ZTIGather.wsf it appears that this occurs because the script is using different methods to set the properties IsVM and IsDesktop.  The chassis type of the Hyper-V VMs is matching those of a Desktop, whilst Model and BIOS description version are also setting the property isVM to true.






 One work-around - involving modifying the customsettings.ini - is described in this technet forum post.

Another work-around, is to modify your task sequence to add a step that sets IsDesktop to False right before the "Format and Partition ... " steps.



Here is the step from the ts.xml:

"
      <step type="SMS_TaskSequence_SetVariableAction" name="HyperV variable set" description="" disable="false" continueOnError="false" successCodeList="0 3010">
        <defaultVarList>
          <variable name="VariableName" property="VariableName">ISDesktop</variable>
          <variable name="VariableValue" property="VariableValue">False</variable>
        </defaultVarList>
        <action>cscript.exe "%SCRIPTROOT%\ZTISetVariable.wsf"</action>
        <condition>
          <expression type="SMS_TaskSequence_WMIConditionExpression">
            <variable name="Namespace">root\cimv2</variable>
            <variable name="Query">SELECT * FROM Win32_ComputerSystem WHERE Model LIKE "%Virtual%"</variable>
          </expression>
        </condition>
      </step>
"

I hope this post will save you some time. Enjoy !! 

Wednesday, May 28, 2014

Powershell: One liner to output logon events including LogonType and UserName

Here is a small powershell command that will extract the latest events of type "logon" or event ID 4624 with their logontype and the TargetUserName. 

get-eventlog -LogName Security -instanceID 4624 -newest 100 | %{ $arrMSG = $_.Message -Split "`n"; $LogonType = $arrMSG[8]; $TargetUserName = $arrMSG[12]; Add-Member -MemberType NoteProperty -Name LogonType -Value $LogonType -InputObject $_; Add-Member -MemberType NoteProperty -Name TargetUserName -Value $TargetUserName -InputObject $_; $_ | select TimeGenerated, InstanceID, LogonType, TargetUserName} | ft -AutoSize -wrap


Info on the logon types and their meaning can be found on the web , i.e.: here.

The same can also be achieved using WMI and  XML but in this case I opted to split the Message property into an array and access the data by row number. Here is a discussion on Technet regarding using WMI instead.

Friday, April 11, 2014

HOW TO: Migrate large VMDK content to VHD when everything else fails

The Task: 
Migrate our lab server (IBM x3655) - so that it supports Windows 8. 8.1 and equivalent server versions - from  ESXi 3.5 Update 5 (Highest version officially compatible with the hardware) to Hyper-V 2012R2.

The Setup: 
Staged a desktop with windows 8.1 and Hyper-V to use as a "pivot" to store my converted VMs and ensure they do work properly before reinstalling the Server OS.

The Problem(s): 
After I had successfully converted over the network my smaller VMs ( Disk of 40GB or less) using 5nineEasyConverter I could not convert the larger VMs this way and would get conversion errors of some sort.
I spent a lot of time testing different products including WinImage and VMDK2VHD but did not have much success.

The work-around : 
Out of options, I decided to boot my VMs on a WinPE image and use IMAGEX to capture their content to a WIM file to then apply this to a new VHD(X).

Note that regardless of the method you use when converting your VMs from one vendor to another always uninstall the "integration" software from the VM before hand. If you use all in one converters like the one mentioned above also ensure your VMs don't have any snapshots, since in this instance we are capturing the disk content, we don't really care about snapshot and this method could be used if you are unable to merge your snapshot with your VM for whatever reason.

Since capturing VMs running in iSCSI mode and migrating those to IDE mode in HyperV, extra steps were required (point #7 and following) to fix the boot error "0xc000000e The boot selection failed because a required device is inaccessible.".

1. Map a share from  in the WinPE environment to the box where you want to store your migrated VMs.
2. Put a copy of Imagex.exe on the WinPE RAM Disk under X:\Windows\System32 for ease of use.
3. Run: Imagex /CAPTURE C:\ Z:\MylargeVM1.WIM "My Large VM OS Disk"
4. Let the capture finish ( it does take a while but less than any of the other products tried before).
5. Create a new VHDX on your "pivot" box with Diskpart, attach it, create a primary partition, format it, make the partition active (else it wont boot either) and assign a letter to it (i.e.: F:).
6. Apply the WIM to the new VHDX by running:  Imagex /Apply D:\MylargeVM1.WIM 1 F:\
   Note: At that point I did detach the VHD, created a new VM in Hyper-V using this existing disk but the VM failed to boot and was throwing an error "0xc000000e The boot selection failed because a required device is inaccessible." The following steps are to apply the fix KB314082 to solve this problem.
7. Mount the SYSTEM hive of your new VHD(X) in the "pivot" box registry editor:
   From Regedit, select HKLM and go to File > Load Hive. Navigate to F:\Windows\System32\config and select SYSTEM. Mount the hive as Z.
8. Copy the entries under "Windows Registry Editor Version 5.00" from KB314082 to a .reg file and replaced "HKEY_LOCAL_MACHINE\SYSTEM" with "HKEY_LOCAL_MACHINE\Z\SYSTEM".
9.  Run the newly created .REG to add the entries to the loaded SYSTEM hive.
10. Unload the Hive, Detach you VHD, Create your VM in HyperV and boot it up. Hopefully you should be all good.

I hope this will be useful to a few and if you have other solutions please do use the comment section to share them! Enjoy !!

Monday, February 3, 2014

Powershell: One Liner to output windows services and their StartMode

Here is a one line Powershell command to output the Windows services and their start-up type (Manual, Auto, Disabled).

Handy to check SOE images and ensure all those services are set consistently in between releases or simply take a dump of those settings on servers to keep track of things.

(Get-Service).name | %{@{$_=(Get-WmiObject -Class Win32_Service -Property StartMode -Filter "Name='$_'").StartMode}}




Edit: Here is a more advanced version of that one liner, with the display name and then the service name ( start-up type) - Service Status.
Notice the use of an hash table and -f option to format the output string and include several object properties.

Get-Service |%{@{$_.DisplayName='{0} ({1}) - {2}' -f $_.Name,(Get-WmiObject -Class Win32_Service -Property StartMode -Filter  "Name='$($_.Name)'").StartMode, $_.status}} | format-Table -AutoSize




Friday, January 17, 2014

PowerShell Script: GPO replication status across Domain Controller

Helloooo !!

A colleague asked me to create a PS script to check for a given GPO its AD and Sysvol versions across all Domain Controllers.

So I wrote this script that utilize the ActiveDirectory and GroupPolicy Module.

Depending on the size of your domain it can take a couple of minutes to contact each DC and retrieve the info, so launch it and go for a coffee or something...

In the same fashion as for the script from my last post, we will again be using a system object to collect all of the data together, which gives you the option to pipe it nicely to display the data or create CSV reports.
So here we go:

What the script does: 

This script takes the name of the GPO you want to check the replication status as an argument. 
It then uses the get-addomaincontroller  cmdlet from the ActiveDirectory Module to gather a list of all domain controllers host name to query. 
The retrieval of the versions on each DC is done using the Get-GPO cmdlet with -server option.

All of it is then wacked into a System.Object which is output at the end of the process. 

It is assumed that the ActiveDirectory and GroupPolicy modules are already imported in your session and that you have set the ExecutionPolicy properly on your system beforehand so that you can run the script locally.

How it works: 

Here are a few syntax example to use the script. 

& Get-GPOReplicationReport.ps1 "My GPO Name_v1.5" | Out-GridView
      The script creates a report for the specified GPO and display it in the out.GridView window.

& Get-GPOReplicationReport.ps1 "My GPO Name_v1.5" | ConverTo-Csv -Delimiter ','
      The script creates a report for the specified GPO and display it in the powershell host window as a comma delimited string (for copy and paste) .

& Get-GPOReplicationReport.ps1 "My GPO Name_v1.5" |  Export-Csv -Delimiter ',' -Path C:\MyGPOReplicationReport.csv
      The script creates a report for the specified GPO and save it as a CSV file.

Download Link:


Script Content:

#Created by toussman@gmail.com on 17/01/2014 
#http://theplatformadmin.blogspot.co.uk/

param(
  [parameter(Mandatory = $TRue )][String]$GPOName
 )

$DCList = (get-addomaincontroller -filter *).hostname 

$colGPOVer = @()

foreach ($DC in $DCList){

$objGPOVers = New-Object System.Object

$GPOObj = Get-GPO $GPOName -server $DC

$UserVersion = [string]$GPOObj.User.DSVersion + ' (AD), ' + [string]$GPOObj.User.SysvolVersion + ' (sysvol)'
$ComputerVersion = [string]$GPOObj.Computer.DSVersion + ' (AD), ' + [string]$GPOObj.Computer.SysvolVersion + ' (sysvol)'

$objGPOVers | Add-Member -type noteproperty -name GPOName -value $GPOName
$objGPOVers | Add-Member -type noteproperty -name DCName -value $DC
$objGPOVers | Add-Member -type noteproperty -name UserVersion -value $UserVersion
$objGPOVers | Add-Member -type noteproperty -name ComputerVersion -value $ComputerVersion

$colGPOVer += $objGPOVers 
}

$colGPOVer | sort-object GPOName, DCName

Well, that's it for this post. 

I hope you will find the script useful and if you have any suggestions or spot something that can be improved leave me a comment to let me know. 

Until next time !! 

Thursday, January 16, 2014

PowerShell Script: HouseKeeping GPO Report

Hello there !! 

My first post for 2014 is going to be about a Powershell script I just created to produce a GPO Report to keep track of the environment and also list GPOs for housekeeping tasks such as listing all the GPOs without WMIfilter, all the GPOs linked to a particular OU, ...

None of the GroupPolicy cmdlets that come with the GroupPolicy Module in RSAT actually allowed me to produce an all in one report based on the actual object configuration (i.e.: OU Linked, WMI filter applied, Security filtering, ... ).

It is assumed that you have imported the GroupPolicy module and that you have set the ExecutionPolicy properly on your system beforehand so that you can run the script locally.

What the script does: 

The script gets a specified list or all of the GPO in a domain and returns the following list of properties per GPO and per OU Link:
GPOName, LinksPath, WmiFilter, CreatedTime, ModifiedTime, ComputerRevisionsAD, ComputerRevisionsSYSVOL, UserRevisionsAD, UserRevisionsSYSVOL, ComputerSettingsEnabled, UserSettingsEnabled, SecurityFilter.

In other words if a GPO is linked to more than one OU it will appear once per OU where it is linked in the report. That is the same as the GPMC display tree view.

The report focuses on what is actually in effect on the domain, therefore:
- Links that are not enabled are discarded automatically.
- Security filtering for groups, users and computers that have been deleted from the domain but are still showing as SID in the GPO are skipped and not showing the report.

How it works: 

Here are a few syntax example to use the script. 

& Get-GPOReport.ps1 -All | Out-GridView
      The script creates a report for all the GPOs on the domain.

& Get-GPOReport.ps1 -GPOList  GPOName1,GPOName2,GPOName3 | ConverTo-Csv -Delimiter ','
      The script creates a report for the specified GPO.

$a = Get-Content C:\MyListOfGPOs.txt 
& Get-GPOReport.ps1 -GPOList $a | Export-Csv -Delimiter ',' -Path C:\MyGPOReport.csv
      The script creates a report for the GPOs specified in C:\MyListOfGPOs.txt.

Download Link:

https://drive.google.com/file/d/0B3ED4HUGG162LTNWWGNRaENVWmc/edit?usp=sharing

Script Content:

#Created by toussman@gmail.com on 16/01/2014 
#http://theplatformadmin.blogspot.co.uk/

param(
[parameter(Mandatory = $False )][array]$GPOList,
    [parameter(Mandatory = $False )][switch]$All
)

if( $All ){$GPOList = (Get-Gpo -All).DisplayName}
If( $GPOList -eq $null){Write-Host "Specify a list of GPOs!!"; Break}

$colGPOLinks = @()

foreach ($GPOItem in $GPOList){
       
    [xml]$gpocontent = Get-GPOReport $GPOItem -ReportType xml
    $LinksPaths = $gpocontent.GPO.LinksTo | ?{$_.Enabled -eq $True} | %{$_.SOMPath}
    $Wmi = Get-GPO $GPOItem | Select-Object WmiFilter
    
    $CreatedTime = $gpocontent.GPO.CreatedTime
    $ModifiedTime = $gpocontent.GPO.ModifiedTime
    
    $CompVerDir = $gpocontent.GPO.Computer.VersionDirectory
    $CompVerSys = $gpocontent.GPO.Computer.VersionSysvol
    $CompEnabled = $gpocontent.GPO.Computer.Enabled
    
    $UserVerDir = $gpocontent.GPO.User.VersionDirectory
    $UserVerSys = $gpocontent.GPO.User.VersionSysvol
    $UserEnabled = $gpocontent.GPO.User.Enabled

    $SecurityFilter = ((Get-GPPermissions -Name $GPOItem -All | ?{$_.Permission -eq "GpoApply"}).Trustee | ?{$_.SidType -ne "Unknown"}).name -Join ','

    foreach ($LinksPath in $LinksPaths){
        $objGPOLinks = New-Object System.Object
        $objGPOLinks | Add-Member -type noteproperty -name GPOName -value $GPOItem
        $objGPOLinks | Add-Member -type noteproperty -name LinksPath -value $LinksPath
        $objGPOLinks | Add-Member -type noteproperty -name WmiFilter -value ($wmi.WmiFilter).Name
        $objGPOLinks | Add-Member -type noteproperty -name CreatedTime -value $CreatedTime
        $objGPOLinks | Add-Member -type noteproperty -name ModifiedTime -value $ModifiedTime
        $objGPOLinks | Add-Member -type noteproperty -name ComputerRevisionsAD -value $CompVerDir
        $objGPOLinks | Add-Member -type noteproperty -name ComputerRevisionsSYSVOL -value $CompVerSys
        $objGPOLinks | Add-Member -type noteproperty -name UserRevisionsAD -value $UserVerDir
        $objGPOLinks | Add-Member -type noteproperty -name UserRevisionsSYSVOL -value $UserVerSys
        $objGPOLinks | Add-Member -type noteproperty -name ComputerSettingsEnabled -value $CompEnabled
        $objGPOLinks | Add-Member -type noteproperty -name UserSettingsEnabled -value $UserEnabled
        $objGPOLinks | Add-Member -type noteproperty -name SecurityFilter -value $SecurityFilter

        $colGPOLinks += $objGPOLinks
    }
}

$colGPOLinks | sort-object GPOName, LinksPath 

Well, that's it for this post. 

I hope you will find the script useful and if you have any suggestions or spot something that can be improved (I am no PowerShell Guru) leave me a comment to let me know. 

Until next time !!