Windows 10 – anniversary update

The new version of Windows 10 has been released. Since the build was completed during the month of July it has been named 1607 and has a build version number of 14393. The new version can be deployed using the current version of MDT but needs an upgraded ADK to fully work. The new ADK can be downloaded from here http://download.microsoft.com/download/9/A/E/9AE69DD5-BA93-44E0-864E-180F5E700AB4/adk/adksetup.exe

Don’t forget to rebuild your reference images to include C++ runtimes and other needed application frameworks for your organization.

If you want to know more about what’s new in ADK check you Johan Arwidmarks blog about that here http://deploymentresearch.com/Research/Post/539/Inside-Windows-ADK-10-v1607

I have not found any new releases of the ADMX templates but the current ones can be used from and installed OS with 1607. You will find them under C:\Windows\PolicyDefinitions.

Happy deploying!

/Peter

Datacenter – Change DNS Server

Let’s not kid ourselfs chaning dns servers happens. There is a new domain controller or someone moves the DNS to a new box with a new IP-address and the pesky job of changing all the primary and secondary DNS entrys on all your member servers has just got dropped into your lap.

Well there is a bright side. You can use PowerShell!

Posted below is a script that will change the primary and secondary dns server entry for a list of computers. The list can be either manual like this invoke-dnsserverchange.ps1 –computername server01.corp.viamonstra.com –primarydns 8.8.8.8 –secondarydns 8.8.4.4 or it could be an txt file with all the server in it like so invoke-dnsserverchange.ps1 c:\myservers.txt –primarydns 8.8.8.8 –secondarydns 8.8.4.4

By default the script will output logfile where it will list all the servers it has tried and notify you on the result for each. The script will not change dnsentries for servers with more than 1 network adapter. This is due to me having no control if you have a server with external dns on one side and internal on the other so those you need to change manually.

Since this requires PowerShell don’t forget that for Server 2008 and R2 you still need to enable and open the firewall for remote PowerShell for this to work.

The Script

<#
Created:     2016-04-15
Version:     1.0
Author :     Peter Lofgren
Twitter:     @LofgrenPeter
Blog   :     https://syscenramblings.wordpress.com

Disclaimer:
This script is provided "AS IS" with no warranties, confers no rights and
is not supported by the author
#>
<#
.SYNOPSIS
  Change DNS Client address on a computer
.DESCRIPTION
  Sets new DNS client ip address on one or more computers
.EXAMPLE
  Invoke-DnsServersChange.ps1 -ComputerName Server01.corp.viamonstra.com -PrimaryDns 8.8.8.8 -SecondaryDNS 8.8.4.4
.EXAMPLE
  Invoke-DnsServersChange.ps1 -ComputerName Server01.corp.viamonstra.com,Server02.corp.viamonstra.com -PrimaryDns 8.8.8.8 -SecondaryDNS 8.8.4.4
.EXAMPLE
  Invoke-DnsServersChange.ps1 -ComputerName C:\Servers.txt -PrimaryDns 8.8.8.8 -SecondaryDNS 8.8.4.4
#>
param (
  [Parameter(Mandatory=$true,Position=0)]
  $ComputerName,
  [Parameter(Mandatory=$true,Position=1)]
  $PrimaryDNS,
  [Parameter(Mandatory=$true,Position=2)]
  $SecondaryDNS,
  [Parameter(Mandatory=$false,Position=3)]
  $LogFile = ".\DnsServers.log"
)
Add-Content -Path $LogFile -Value "Starting DNS set run at $(Get-Date -Format yyy-MM-dd) $(Get-Date -Format HH:mm)" -Force
Add-Content -Path $LogFile -Value "ComputerName,PrimaryDNS,SecondaryDNS,Result" -Force

if ((Test-path -Path $ComputerName) -eq $true) {
  $ComputerName = Get-Content -Path $ComputerName
}

if ($ComputerName.count -eq 1 -and $ComputerName -eq $env:COMPUTERNAME) {
  if ((Get-NetAdapter).count -ge 2) {
    $Result = "$env:COMPUTERNAME,FAILED,FAILED,Multiple Adapters found"
  }
  Else {
    $InterfaceIndex = (Get-NetAdapter -Physical).InterfaceIndex
    Set-DnsClientServerAddress -InterfaceIndex $InterfaceIndex -ServerAddresses $PrimaryDNS,$SecondaryDNS
    $Result = "$env:COMPUTERNAME,$PrimaryDNS,$SecondaryDNS,SUCCESS"
  }
  Add-Content -Path $LogFile -Value $Result -Force
}
Else {
  foreach ($Computer in $ComputerName) {
    if ($Computer -eq $env:COMPUTERNAME) {
      if ((Get-NetAdapter).count -ge 2) {
        $Result = "$env:COMPUTERNAME,FAILED,FAILED,Multiple Adapters found"
      }
      Else {
        $InterfaceIndex = (Get-NetAdapter -Physical).InterfaceIndex
        Set-DnsClientServerAddress -InterfaceIndex $InterfaceIndex -ServerAddresses $PrimaryDNS,$SecondaryDNS
        $Result = "$env:COMPUTERNAME,$PrimaryDNS,$SecondaryDNS,SUCCESS"
      }
      Add-Content -Path $LogFile -Value $Result -Force
    }
    Else {
      $Result = Invoke-Command -ComputerName $Computer -ScriptBlock {
        param (
        $PrimaryDNS,
        $SecondaryDNS
        )
        if ((Get-NetAdapter).count -ge 2) {
          return "$env:COMPUTERNAME,FAILED,FAILED,Multiple Adapters found"
        }
        Else {
          $InterfaceIndex = (Get-NetAdapter -Physical).InterfaceIndex
          Set-DnsClientServerAddress -InterfaceIndex $InterfaceIndex -ServerAddresses $PrimaryDNS,$SecondaryDNS
          return "$env:COMPUTERNAME,$PrimaryDNS,$SecondaryDNS,SUCCESS"
        }
      } -ArgumentList $PrimaryDNS,$SecondaryDNS -ErrorAction SilentlyContinue
      if ($? -eq $false) {
        Add-Content -Path $LogFile -Value "$Computer,FAILED,FAILED,Failed to connect" -Force
      }
      else {
        Add-Content -Path $LogFile -Value $Result -Force
      }
    }
  }
}
Add-Content -Path $LogFile -Value "Finished DNS set run at $(Get-Date -Format yyy-MM-dd) $(Get-Date -Format HH:mm)"

 

Happy deploying!
Peter

Enable credential guard in configmgr

While working with at customer last we it was decided they wanted Credential Guard. Which in it self is a good thing. The problem was that they wanted this enabled as part of the Configuration Manager OSD.

Now normally automating things during ConfigMgr OSD isn’t to difficult however ConfigMgr has a problem with things that require double reboots. Since Hyper-V is a prerequisite for Credential Guard and Hyper-V requires a double reboot this poses a problem.

This might be solved by Microsoft in the future but for now you will have to employ a bit of a workaround. This consists of a couple of things, one is setting it up so you have a reboot not monitored by the task sequence and the other is installing the required roles and lastly you will also need to input the relevant registry values to enable the features.

Step 1 – Adding a reboot outside of the task sequence

This is something you should probably do anyway and it is documented in several blogpost before this one.

You will need to set a custom task sequence variable called SMSTSPostAction and set that to “Shutdown /r /t 30” this will cause a reboot 30 seconds after sequence thinks its done.

SMSTSPostAction

Step 2 – Creating the package

Download the script from here http://bit.do/bYZsr and put it in a folder on your CMSources share. Create a new package and a program and define the following as command line for running it: “PowerShell.exe –ExecutionPolicy ByPass –file “Enabled-CredentialGuard.ps1”

Don’t forget to enabled “Allow this program to be installed from the Install Package task sequence without being deployed”

Step 3 – Customize the task sequence

Lastly we customize the sequence to run this specific package at specific point in the sequence. The rule here is that it needs to be run after any other steps that can cause a reboot as the script will install and configure everything but the reboot should happen outside of the sequence as we configured it during step 1.

So for this customer that happens just before status is set to 5 as you can se in the picture below.

Sequence

The last customization is to set an option on this to check for a task sequence variable. You should check for isUEFI equals true. This is to make this only applied to UEFI based machines as it will not work on legacy bios. If you want to you can add steps to check for Secureboot or other pre reqs.

UEFI

The script – raw

<#
Created:     2016-04-02
Version:     1.0
Author :     Peter Lofgren
Twitter:     @LofgrenPeter
Blog   :     https://syscenramblings.wordpress.com

Disclaimer:
This script is provided "AS IS" with no warranties, confers no rights and
is not supported by the author
#>

Function Import-SMSTSENV{
    try
    {
        $tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
        Write-Output "$ScriptName - tsenv is $tsenv "
        $MDTIntegration = "YES"
       
        #$tsenv.GetVariables() | % { Write-Output "$ScriptName - $_ = $($tsenv.Value($_))" }
    }
    catch
    {
        Write-Output "$ScriptName - Unable to load Microsoft.SMS.TSEnvironment"
        Write-Output "$ScriptName - Running in standalonemode"
        $MDTIntegration = "NO"
    }
    Finally
    {
    if ($MDTIntegration -eq "YES"){
        if ($tsenv.Value("LogPath") -ne "") {
          $Logpath = $tsenv.Value("LogPath")
          $LogFile = $Logpath + "\" + "$LogName.log"
        }
        Elseif ($tsenv.Value("_SMSTSLogPath") -ne "") {
          $Logpath = $tsenv.Value("_SMSTSLogPath")
          $LogFile = $Logpath + "\" + "$LogName.log"
        }
    }
    Else{
        $Logpath = $env:TEMP
        $LogFile = $Logpath + "\" + "$LogName.log"
    }
    }
}
Function Start-Logging{
    start-transcript -path $LogFile -Force
}
Function Stop-Logging{
    Stop-Transcript
}

 

# Set Vars

$SCRIPTDIR = split-path -parent $MyInvocation.MyCommand.Path
$SCRIPTNAME = split-path -leaf $MyInvocation.MyCommand.Path
$SOURCEROOT = "$SCRIPTDIR\Source"
$SettingsFile = $SCRIPTDIR + "\" + $SettingsName
$LANG = (Get-Culture).Name
$OSV = $Null
$ARCHITECTURE = $env:PROCESSOR_ARCHITECTURE
$LogName = $SCRIPTNAME

 

#Try to Import SMSTSEnv
. Import-SMSTSENV

 

#Start Transcript Logging
. Start-Logging

 

#Output base info
Write-Output ""
Write-Output "$ScriptName - ScriptDir: $ScriptDir"
Write-Output "$ScriptName - SourceRoot: $SOURCEROOT"
Write-Output "$ScriptName - ScriptName: $ScriptName"
Write-Output "$ScriptName - SettingsFile: $SettingsFile"
Write-Output "$ScriptName - Current Culture: $LANG"
Write-Output "$ScriptName - Integration with MDT(LTI/ZTI): $MDTIntegration"
Write-Output "$ScriptName - Log: $LogFile"

 

#Enable Hyper-V
If ([environment]::Is64BitOperatingSystem -eq $True) {
  $InstallerName = "C:\Windows\sysnative\dism.exe"
}
Else {
  $InstallerName = "C:\Windows\system32\dism.exe"
}
$Arg = "/online /enable-feature /featurename:Microsoft-Hyper-V-Hypervisor /all /LimitAccess /Norestart"
Write-Output "About to run $InstallerName with arguments $Arg"
$Result = Start-Process -FilePath $InstallerName -ArgumentList $Arg -NoNewWindow -Wait -PassThru
Write-Output "Finsihed installing Hyper-V-Hypervisor with exitcode $($Result.ExitCode)"

$Arg = "/online /enable-feature /featurename:IsolatedUserMode /LimitAccess /Norestart"
Write-Output "About to run $InstallerName with arguments $Arg"
$Result = Start-Process -FilePath $InstallerName -ArgumentList $Arg -NoNewWindow -Wait -PassThru
Write-Output "Finsihed installing IsolatedUserMode with exitcode $($Result.ExitCode)"

$Arg = "/online /disable-feature /featurename:Microsoft-Hyper-V-Tools-All /Norestart"
Write-Output "About to run $InstallerName with arguments $Arg"
$Result = Start-Process -FilePath $InstallerName -ArgumentList $Arg -NoNewWindow -Wait -PassThru
Write-Output "Finsihed removing Hyper-V Tools with exitcode $($Result.ExitCode)"

#Enable Credential Guard
$Path = "HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard"
New-Item -Path $Path -ItemType Directory -Force -ErrorAction SilentlyContinue
New-ItemProperty -Path $Path -Name EnableVirtualizationBasedSecurity -PropertyType 4 -Value 1 -ErrorAction SilentlyContinue
New-ItemProperty -Path $Path -Name RequirePlatformSecurityFeatures -PropertyType 4 -Value 1 -ErrorAction SilentlyContinue
New-ItemProperty -Path $Path -Name HypervisorEnforcedCodeIntegrity -PropertyType 4 -Value 0 -ErrorAction SilentlyContinue

New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\Lsa -Name LsaCfgFlags -PropertyType 4 -Value 1 -ErrorAction SilentlyContinue

 

#Stop Transcript Logging
. Stop-Logging

ConfigMgr–Extending Hardware Inventory

So if you are using ConfigMgr you probably know that you can extend hardware inventory to inventory pretty much anything. The cool thing is that by almost default there are two classes that are really nice to have.

The first being Win32_QuickFixEngineering which is a WMI class listing all installed patches. Having that inventoried means you can build collections based on missing or certain patches installed which when it comes to critical patches or hotfixes are really nice.

The second is a kind of custom class and its called Microsoft_BDD_Info and it is created by ZTITatoo.wsf when you deploy a computer with either MDT or ConfigMgr with MDT integration. This includes a bunch of information from the deployment. For instance it lists which sequence ID was run and the timestamp. So if you inventory this you can keep collections based on what sequences was used and if you have a nice version control in your sequence you now find all your computer that where deployed with a certain version of sequence.

So how do you enable this awesomeness? Well its pretty simple. Fire off your ConfigMgr console and check under Administration and Client settings. You will have a setting called Default Client Settings. Open it up and on the left hand click Hardware inventory. Next click the button to the right where it says Classes. You will be presented with a long list of WMI classes that can be enabled and disabled. At the top just do a search for “Quick” and select the class Win32_QuickFixEngineering.

QuickFix

To enabled the second class a bit more work is required but don’t worry it is not hard. Go back to the client settings (if you left it) and click the Add button seen at the bottom (look at the image above for guidance). You now need to connect to a computer that has been deployed using MDT/ConfigMgr+MDT so click the connect button and type in the computer name and credentials if needed.

HardwareClass

When connected you will see all the WMI classes available on that computer. Just find the one called Microsoft_BDD_Info and select it and click OK when done.

BDD_info

So now they have both been selected and you have saved the Clients Settings by clicking OK all the way out. Now all that remains is waiting for the next hardware inventory to complete and you can start using the values collected.

Happy deploying!

/Peter

PowerShell Wrapper – Windows Management Framework 4.0

I got a question (you know who you are) if I had a WMF 4 PowerShell wrapper and while I didn’t at the time I thought I would create one as you still need WMF4 to be installed before WMF 5 can be installed.

Create a simple folder structure looking like this

Folders

In the source folder place the files for WMF4 that can be downloaded from Microsoft Download here https://www.microsoft.com/en-us/download/details.aspx?id=40855

Download the script here http://bit.ly/1TAck2I

Import as an application into MDT and either dynamically assign the application or add it into your Task Sequence the same way I do in my Windows Management Framework 5 here https://syscenramblings.wordpress.com/2016/02/26/wmf-5-in-the-reference-image/

Happy deploying!

/Peter

creating a outlook signature with logo

The other week I was working at a customer and the requested that we rebuild the way they create their Outlook signature from the old word document template. And as we can use PowerShell for everything today that’s where we went. The problem with doing it this way is that the logo image was residing on a file share in their internal environment. This means as soon as the email is sent the image is not attached and the receiving end could not the their logo.

So to fix this there is a small registry value you can set to make sure outlook always includes images. You can also find the complete script here.

First off we need to create a folder structure cause the script has another feature which they requested. So create a folder and inside that folder place the script, your logo image file and create another folder inside named Pictures.

Like this:Folder

Open up the script and at the top of the script change two things, first change the name of the logo file to whatever your file is named and change the name of the signature to something fitting your environment. The name you type in here is the one that will show up if select a different signature in Outlook.

Next Check the bottom of the script there are two sections one being the registry settings to make sure to include the image and the other is a value to defined if the signature should be used by default. If you wish to use this just uncomment the lines creating each of the registry values.

Now just run the script!
That’s it to creating the signature. If you want to customize the look of the signature its a common html creation so pretty much anything is possible.

It should give you a signature looking something like this.

Clean

So how does it work? Well we start by getting the username of the current user and then do an AD query for that user and get the above values. This means that whatever is in Active Directory will be in the signature creating a streamlined and unified look for every person in your organization.

Once that is done it will get the logo picture and insert that as well. Next comes the nice extra feature with the Picture folder you created. All images you add in here will be added to the bottom of the signature. So now the question is why would you want that? Well a number of customers I work with like to add a banner at the end during certain times of the year or when they want to push a certain product or happening. Now it can be centrally controlled and all users will get it.

Just don’t forget to remove old pictures in the folder.

So to finish up, deployment!
To accomplish this I create a GPO than runs a login script. If you don’t want to do that you can always do a scheduled task that checks a certain network folder and runs the script or any other preferred solution.

Link to the script is here http://bit.ly/1TAcpU6

Hope this helps and happy deploying!

/Peter

MDT handoff to SCCM

I will start by saying this is not in any of the best practices books but it works well and is used for certain scenarios.

Sometimes when I get to a customer they have MDT setup and working for OSD but someone higher up have decided that they need ConfigMgr to manage clients going forward. Don’t get me wrong I’m all for using ConfigMgr to manage clients but that being said not everyone finds ConfigMgr the easiest or most understandable platform to use. So the question then arises “Could we still use MDT to deploy the machines and then ConfigMgr to manage them?” and of course the answer is YES!

So how do we accomplish this? There are two ways and I will describe both but only show one.

The first way of doing it by using the excellent startup script created by Jason Sandys (found here http://blog.configmgrftw.com/configmgr-client-startup-script/) it is easy to setup and only requires a small startup GPO and a file share. The upside to using this is that if someone for some reason didn’t get the agent during initial setup or someone uninstalled it from a client that is targeted by the GPO the client will get reinstalled. Jason has also managed to add some repair functions to it. So the downside then is that when using a GPO the client has to actually read the GPO and for that work the client has to be a member of the domain so workgroup computers are out.

The second way is what we are going to focus on for the rest of this post. That way is to install it during OSD in MDT as an application. The upside to doing it this way is, as soon as the deployment is done the client is also installed regardless of if the client joins a domain or not. Another upside to doing it this way instead of with a GPO is that if the client restarts at any point during deployment and the GPO is enabled the client will be installed during OSD possibly messing around while you are doing other installations or configuration steps.

So how do I do this? Well first off we need to create an application in MDT then we link that application into our sequence.

Step 1 – Creating the application

Create a folder named “CMAgent” so we have something to work with. Inside that create another folder called “Source”. Next to the Source folder place the script file and the xml which you download a bit further down in the post. In the Source folder you copy the client installation files from your site server in \\<your site server>\sms_<sitecode>\Client.

You should then have a folder that looks like this

Folder

Now we import that into MDT. So you give the application a name, point to your source folder and set a command line. For name I prefer Install – CCM Agent so I can easily see what the application does by just looking at the name. For command line you should use the following

PowerShell.exe –ExecutionPolicy ByPass –File Install-Application.ps1

If you open the application when its done it should look like this

Application

Step 2 – Adding it to the Task Sequence

The next bit is to add it to the sequence in the correct spot to avoid it being installed and then messing with your deployment. So open your sequence go down all the way to the end and mark the step called Apply Local GPO Package, Click Add at the top and Create a group. Now name the group so you know what it does, either Custom Steps or as in this case I named it Custom Handoff. In that group we add a step for Install application. Change the step to install a single application and point to your newly imported application.

The sequence should then look something like this

Sequence

Step 3 – Customizing the agent installation

The last thing you need to do is change some settings to point the agent to your specific environment. So open up your deploymentshare folder and browse to Applications\Install – CCM Agent. Use notepad to edit the settings.xml file and change the Installswitch section of the file. Below is a sample of how it can look, make sure to change it to suit your server name and infrastructure.

settings

Your all set! Next time you image a computer it will then have the CCM agent installed.

Link to download the script is here http://bit.ly/1TAczuB

Happy deploying!

/Peter