WS2016 Beyond Unsupported

Windows Server 2016 Eval media has been released and while we wait for VL media there is a small cheat you can use if you want to play around with the licensing modes. Please do note that this is not in any way a supported way to do it and far from recommended.

Method 1 – An already running server with WS 2016 installed

This is the easier way and can be done on any running Windows Server 2016 except for domain controllers.

First you need to get the KMS keys from Microsoft TechNet KMS key appendix A found here https://technet.microsoft.com/en-us/library/jj612867(v=ws.11).aspx. There are different keys for both Standard and Datacenter so make sure pick the correct one.

Next start an elevated command prompt and run one of the following commands depending on if you upgrading to Standard or Datacenter

For Standard:
Dism /online /Set-Edition:ServerStandard /Productkey:<key for Standard from appendix A> /AcceptEULA /Norestart

SrvStd

For Datacenter:
Dism /online /Set-Edition:ServerDatacenter /Productkey:<key for Datacenter from appendix A> /AcceptEULA /Norestart

SrvDC

Wait while it completes and then reboot the server and your done. When the server is done rebooting you will have either a Standard edition or Datacenter Edition server.

Method 2 – Changing the media and enable in-place upgrade

This is a bit more complex and requires some installation steps before it works. The first thing needed is the newest Dism tools 10.0.14939 found in the ADK for Windows 10 1607. That can be found here https://developer.microsoft.com/sv-se/windows/hardware/windows-assessment-deployment-kit

Download the setup for ADK and run the installation. The only component needed for this is the Deployment Tools. Wait for the installation to finish and then reboot the machine to make sure all dlls are registered.

adk

Download the Eval media from the TechNet Evaluation Center https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016

Copy the contents of the ISO to a folder on your management machine in this example I will use C:\ISO but you can use any folder, just make sure to correct all paths in each command.

Iso

Create a folder for mounting the wim file (C:\Mount) and start an elevated command prompt.

Change the directory to your newly installed dism tools usually here C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM then run the following commands and press enter after each (there will be some wait in between each)

dism.exe /Mount-Wim /WimFile:C:\ISO\Sources\Install.wim /index:1 /MountDir:C:\Mount

dism.exe /Image:C:\Mount /Set-Edition:ServerStandard /ProductKey:WC2BQ-8NRM3-FDDYY-2BFGV-KHKQY /AcceptEula /Norestart

dism.exe /UnMount-image /MountDir:C:\Mount /Commit

dism.exe /Mount-Wim /WimFile:C:\ISO\Sources\Install.wim /index:2 /MountDir:C:\Mount

dism.exe /Image:C:\Mount /Set-Edition:ServerStandard /ProductKey:WC2BQ-8NRM3-FDDYY-2BFGV-KHKQY /AcceptEula /Norestart

dism.exe /UnMount-image /MountDir:C:\Mount /Commit

dism.exe /Mount-Wim /WimFile:C:\ISO\Sources\Install.wim /index:3 /MountDir:C:\Mount

dism.exe /Image:C:\Mount /Set-Edition:ServerDatacenter /ProductKey:CB7KF-BWN84-R7R2Y-793K2-8XDDG /AcceptEula /Norestart

dism.exe /UnMount-image /MountDir:C:\Mount /Commit

dism.exe /Mount-Wim /WimFile:C:\ISO\Sources\Install.wim /index:4 /MountDir:C:\Mount

dism.exe /Image:C:\Mount /Set-Edition:ServerDatacenter /ProductKey:CB7KF-BWN84-R7R2Y-793K2-8XDDG /AcceptEula /Norestart

dism.exe /UnMount-image /MountDir:C:\Mount /Commit

If you look closely you will notice I change the index number and the product key to update all 4 scenarios, Server Standard Core, Server Standard GUI, Datacenter Core and Datacenter GUI

Now the last step is the one you have to solve yourself and that is to create a bootable ISO from these files.

 

As a last note: DO NOT under any circumstances use this in production. I highly doubt this is a supported or even recommended way from Microsoft but can help you in your testing with licenses.

Happy deploying!

/Peter

Windows Server 2016 Ref Image

During Ignite Windows Server 2016 was released as an Eval product. This means you can now download and start testing the RTM version of 2016 and prepare for when the volume license bits arrive sometime later this fall.

As with previous version of Windows Server it makes sense to create a reference image to include needed zero day patches and Visual C++ runtimes for any applications you might need to run.

To create a reference image we use Microsoft Deployment Toolkit and guidance on how to set that up can be found on TechNet here: https://technet.microsoft.com/en-us/itpro/windows/deploy/create-a-windows-10-reference-image The same principals for Windows 10 applies to Windows Server 2016 with a few differences.

So lets start with importing the operating system this is the same as on the client side, just keep in mind to keep the folder name short to avoid issues with filenames in subfolder getting to long.

ImportOS

Next we create the a Package folder and import the zero day patch with fixes for Storage Spaces Direct (S2D). The patch is at current writing missing a knowledge article but can be found in the update catalog. Search for KB3192366 or use this link http://catalog.update.microsoft.com/v7/site/Search.aspx?q=3192366

When the folder has been created and the patch imported it should look something like this

Package

Continue with creating a Selection Profile to make sure that when the image is deployed only the relevant patches for WS2016 is imported. Expand the Advanced Configuration in MDT, select the node called Selection Profiles. Create a new profile and select the folder created in the step above.

SelectionProfile

We also need an application to install the Visual C++ runtimes to make it as easy as possible use the following from the friendly bunny https://deploymentbunny.com/2014/09/25/nice-to-havevb-script-wrapper-for-all-vc-installers-to-be-used-in-mdt/

VisualC

Next you need to create the task sequence by following the short wizard. When the sequence is created there are a couple of things to sort out.

First off we need to use the selection profile we created earlier. To do that open up the sequence and in the Preinstall section find the step called Apply Patches. To the right you will find a dropdown that is preset to All Packages, this needs to be changed to the Selection Profile created.

TSSelectionProfile

The second item to change is to turn on Windows Update in the sequence which is disabled by default. The two Windows Update steps can be found in the State Restore phase. Make sure to untick the Disable this step check box for each of them.

WindowsUpdate

Add the C++ runtimes application to your sequence just above the first Windows Update step to make sure that any patches available for them will be applied as well.

VisualCTaskSequence

The last thing is to change the default behavior of Windows Update. To to that we need to change a value in the unattend.xml file used by this sequence. Browse to your deployment share and to the Control folder. In here there will be a folder with the same name as the ID of your newly created sequence. Inside of that folder you will find the unattend.xml file, edit the file with Notepad or any other xml compatible editor.

Find the OOBE Section and the value called ProtectYourPC. Change the value from 1 to 3. This will disable Windows Update until MDT is ready to use it and MDT will the turn the feature back on.

unattend

That’s it your all set. This can now be run as part of your image factory setup, as a stand alone sequence with either VmWare or Hyper-V as the virtual machine platform.

If you want more information on the Image Factory check Mikes blog here https://deploymentbunny.com/2014/01/06/powershell-is-king-building-a-reference-image-factory/

And if you want more detailed information on the setup and how to skip wizard panes during your reference image creation check Johans blog here http://deploymentresearch.com/Research/Post/521/Back-to-Basics-Building-a-Windows-7-SP1-Reference-Image-using-MDT-2013-Update-2

Happy deploying!

/Peter

Credential Guard without tools

When you deploy new machines with Windows 10 1607 and want to enable Credential Guards one of the things will want to do is prepare Hyper-V and Isolated User Mode so it is preinstalled so the end users do not get affected during enablement.

First off lets talk about Isolated User Mode, this was previously a stand alone feature that was required but starting with v1607 this has been included into the Hyper-V role. This means that there is one less feature for you to enable and keep track of.

Next we need to enable Hyper-V and the only features you need are the Hyper-V services and Hyper-V platform. This can be achieved using the Install Roles and Features step in MDT. In your sequence before the Windows Update step add a group and add the steps as show below.

Start with a Install Roles and Features step and then add a Restart Computer step and finish with Run Command Line step. Configure the Install Roles and Feature step as follow, check Hyper-V Platform, Hyper-V Hypervisor and Hyper-V Services.

AddHyperV

For the Run Command Line step add the following information:
Dism /online /disable-feature /featurename:Microsoft-Hyper-V-Tools-All /Norestart

RemoveTools

This will ensure that when the computer is finished deploying it will have the necessary roles and features for credential guard but end users won’t see the management tools.

 

Happy Deploying

/Peter

PowerShell – Convert To Weblink

During a recent project I was presented with a long text that was supposed to be emailed to administrative staff. The problem with the text is that it contained links to webpages. And since it was delivered as a text those links didn’t not get converted to HTML formattedformated links in the email.

The Input

The input can be any text that is delivered as a string.
For example:

$InputText = @"
This is meaningful text that someone important has written

A bit down there is a link in the text to http://wwww.viamonstra.com

Regards,
Viamonstra team
http://www.viamonstra.com
"@

The Script

<#
Created:     2016-09-22
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
#>

Function Convert-Weblink($InputText) {
  $regex = "(ht|f)tp(s?)\:\/\/(([a-zA-Z0-9\-\._]+(\.[a-zA-Z0-9\-\._]+)+)|localhost)(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&amp;%\$#_]*)?([\d\w\.\/\%\+\-\=\&amp;\?\:\\\&quot;\'\,\|\~\;]*)"
  $Matches = (Select-String -InputObject $InputText -Pattern $regex -AllMatches).Matches | select -Unique
  foreach ($Match in $Matches) {
    $InputText = $InputText -replace [regex]::Escape($Match.Value),"<a href=$($Match.value)>$($Match.value)</a>"
  }
  Return $InputText
}

The Output

Using the example input text above the output would look something like this and you can the use that to build a webpage, send a HTML formatted email or any other HTML scenario you can think of.

This is meaningful text that someone important has written

A bit down there is a link in the text to <a href=http://wwww.viamonstra.com>http://wwww.viamonstra.com</a&gt;

Regards,
Viamonstra team
<a href=http://www.viamonstra.com>http://www.viamonstra.com</a&gt;

 

Hopefully this helps you out and let me know if you find any issues!

/Peter

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