Jekyll2018-06-10T09:07:38-05:00https://www.bladefirelight.com/PowerShell JourneyRandom musings of a IT ProDavid JonesBladeFireLight@outlook.comAzure Automation to Migrate Mailboxes to Office 3652018-06-07T21:18:00-05:002018-06-07T21:18:00-05:00https://www.bladefirelight.com/nuggets/azure-automation-to-migrate-mailboxes-to-office-365<p class="notice--info"><strong>Note</strong> Some code blocks on this page have word wrap turned on.</p>
<h2 id="the-problem">The problem</h2>
<p>While azure automation is a powerfull platform.
It has some limitations in PowerShell that makes working with Exchange online dificult.
The main issue is the inability to use <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/import-pssession?view=powershell-6">Import-PsSession</a>.
This can be gotten arround with some creative use of variables.</p>
<h2 id="the-goal">The goal</h2>
<p>I recently had to setup a way to the helpdesk to be able to migrate users to the cloud without giving them rights in exchange or a complicated interface.
The moves would need to be both directions and they needed a way to check on the status of the move.
Azure automation seemed like a good fit.</p>
<h2 id="show-me-some-code">Show me some code</h2>
<p>Now I started with some colecting the paramerters, connecting to Azure so I can retrive the credentials from the azure credential store.
This part was fairly strait forward.</p>
<p class="notice--warning"><strong>Note</strong> that I did not make the Identity paramater as an array like you would normaly do in an advanced function.
This is because I wanted to use Flow on the front end and it’s Email selction returns a string of ; seporated email addresses.</p>
<h3 id="param-and-connecting-to-azure">Param and connecting to Azure</h3>
<div class="language-powershell pre-wrap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span><span class="na">CmdletBinding</span><span class="o">(</span>
<span class="na">SupportsShouldProcess</span> <span class="o">=</span> <span class="nv">$true</span>,
<span class="na">PositionalBinding</span> <span class="o">=</span> <span class="nv">$true</span>,
<span class="na">ConfirmImpact</span> <span class="o">=</span> <span class="s1">'Medium'</span><span class="o">)]</span>
<span class="k">Param</span>
<span class="o">(</span>
<span class="c1"># UPN or Email address</span>
<span class="o">[</span>Parameter<span class="o">(</span>
Mandatory <span class="o">=</span> <span class="nv">$true</span>,
ValueFromPipeline <span class="o">=</span> <span class="nv">$true</span>,
ValueFromPipelineByPropertyName <span class="o">=</span> <span class="nv">$true</span>,
Position <span class="o">=</span> 0,
HelpMessage <span class="o">=</span> <span class="s2">"UPN or Email address"</span>
<span class="o">)]</span>
<span class="o">[</span><span class="kt">string</span><span class="o">]</span>
<span class="nv">$Identity</span>,
<span class="o">[</span>Parameter<span class="o">(</span>
Position <span class="o">=</span> 1,
HelpMessage <span class="o">=</span> <span class="s2">"Migration Direction : ToCloud or ToExchange"</span>
<span class="o">)]</span>
<span class="o">[</span>ValidateSet<span class="o">(</span><span class="s2">"ToCloud"</span>, <span class="s2">"ToExchange"</span><span class="o">)]</span>
<span class="o">[</span><span class="kt">string</span><span class="o">]</span>
<span class="nv">$Direction</span> <span class="o">=</span> <span class="s1">'ToCloud'</span>
<span class="o">)</span>
<span class="nv">$CredName</span> <span class="o">=</span> <span class="s1">'365MigrationCredName'</span>
<span class="nv">$connectionName</span> <span class="o">=</span> <span class="s2">"AzureRunAsConnection"</span>
<span class="nv">$IdentityList</span> <span class="o">=</span> @<span class="o">()</span>
<span class="nv">$IdentityList</span> <span class="o">=</span> <span class="nv">$Identity</span> -split <span class="s1">';'</span>
<span class="k">try</span> <span class="o">{</span>
<span class="nv">$servicePrincipalConnection</span> <span class="o">=</span> Get-AutomationConnection -Name <span class="nv">$connectionName</span>
<span class="nb">Write-verbose</span> <span class="s2">"Logging in to Azure..."</span> -Verbose
<span class="nv">$AzureRmAccountParam</span> <span class="o">=</span> @<span class="o">{</span>
ServicePrincipal <span class="o">=</span> <span class="nv">$true</span>
TenantId <span class="o">=</span> <span class="nv">$servicePrincipalConnection</span>.TenantId
ApplicationId <span class="o">=</span> <span class="nv">$servicePrincipalConnection</span>.ApplicationId
CertificateThumbprint <span class="o">=</span> <span class="nv">$servicePrincipalConnection</span>.CertificateThumbprint
<span class="o">}</span>
<span class="nv">$null</span> <span class="o">=</span> Add-AzureRmAccount @AzureRmAccountParam
<span class="o">}</span>
<span class="k">catch</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(!</span><span class="nv">$servicePrincipalConnection</span><span class="o">)</span> <span class="o">{</span>
<span class="nv">$ErrorMessage</span> <span class="o">=</span> <span class="s2">"Connection </span><span class="nv">$connectionName</span><span class="s2"> not found."</span>
<span class="k">throw</span> <span class="nv">$ErrorMessage</span>
<span class="o">}</span>
<span class="k">else</span> <span class="o">{</span>
<span class="nb">Write-Error</span> -Message <span class="nv">$_</span>.Exception
<span class="k">throw</span> <span class="nv">$_</span>.Exception
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>When working with mailbox migrations in PowerShell you have to use two diferent credentials.
One for office 365 in the user@domain format and one for Exchange using the domain/username format.
In my case this was the same user syncronized between on-premises AD and Azure AD.
For this reason I only need one credential and only the basic username needs to be stored.
I will create the two credentials from it.</p>
<h3 id="retriveing-credentials">Retriveing credentials</h3>
<div class="language-powershell pre-wrap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">Write-verbose</span> <span class="s2">"Getting Credentials ..."</span> -Verbose
<span class="nv">$Credential</span> <span class="o">=</span> Get-AutomationPSCredential -Name <span class="nv">$CredName</span>
<span class="nb">Write-verbose</span> <span class="s2">"Credential Loaded : </span><span class="k">$(</span><span class="nv">$Credential</span>.UserName<span class="k">)</span><span class="s2">"</span> -Verbose
<span class="k">if</span> <span class="o">((</span><span class="nv">$Credential</span>.UserName -like <span class="s2">"*@*"</span><span class="o">)</span> -or <span class="o">(</span><span class="nv">$Credential</span>.UserName -like <span class="s2">"*\*"</span><span class="o">))</span> <span class="o">{</span>
<span class="k">Throw</span> <span class="s2">"Credential username must be the username only!. do not include the domain"</span>
<span class="o">}</span>
<span class="nv">$o365Cred</span> <span class="o">=</span> <span class="nb">New-Object </span>System.Management.Automation.PSCredential <span class="o">(</span><span class="s2">"</span><span class="k">$(</span><span class="nv">$Credential</span>.UserName<span class="k">)</span><span class="s2">@mydomain.com"</span>, <span class="nv">$Credential</span>.Password<span class="o">)</span>
<span class="nv">$exchCred</span> <span class="o">=</span> <span class="nb">New-Object </span>System.Management.Automation.PSCredential <span class="o">(</span><span class="s2">"mydomain\</span><span class="k">$(</span><span class="nv">$Credential</span>.UserName<span class="k">)</span><span class="s2">"</span>, <span class="nv">$Credential</span>.Password<span class="o">)</span>
<span class="nb">Write-verbose</span> <span class="s2">"Credential Loaded : </span><span class="k">$(</span><span class="nv">$o365Cred</span>.UserName<span class="k">)</span><span class="s2">"</span> -Verbose
<span class="nb">Write-verbose</span> <span class="s2">"Credential Loaded : </span><span class="k">$(</span><span class="nv">$exchCred</span>.UserName<span class="k">)</span><span class="s2">"</span> -Verbose
</code></pre></div></div>
<p>Now I’m going to connect to office 365.</p>
<div class="language-powershell pre-wrap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">Write-verbose</span> <span class="s1">'Connecting to office365 ...'</span> -Verbose
<span class="nv">$OnlineSession</span> <span class="o">=</span> New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri <span class="s1">'https://outlook.office365.com/powershell-liveid/'</span> -Authentication Basic -Credential <span class="nv">$o365Cred</span> -ErrorAction Stop
<span class="nb">Write-verbose</span> <span class="s1">'Importing PowerShell Commands ...'</span> -Verbose
<span class="c1">#Import-PSSession -Prefix "Online" $OnlineSession</span>
</code></pre></div></div>
<p>Notice that the import sesion is commented out.
That is becuase implicit remoteing is disable or not functional in Azure Automation at the time of this writing.
Please <a href="https://feedback.azure.com/forums/246290-automation/suggestions/13797918-enable-implicit-remoting">up vote the issue</a></p>
<p>I will loop through the identity list</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">Foreach</span> <span class="o">(</span><span class="nv">$user</span> <span class="k">in</span> <span class="nv">$IdentityList</span><span class="o">)</span> <span class="o">{</span>
</code></pre></div></div>
<p>With Import-PSSession out, and <a href="https://docs.microsoft.com/en-us/powershell/module/Microsoft.PowerShell.Core/Import-Module?view=powershell-6">Import-Module</a>(Import-PSSession …) being flaky at best.
I have to fall back to <a href="https://docs.microsoft.com/en-us/powershell/module/Microsoft.PowerShell.Core/Invoke-Command?view=powershell-6">Invoke-Command</a>
I’m going to start with checking if the mailbox exists in the cloud with <a href="https://docs.microsoft.com/en-us/powershell/module/exchange/mailboxes/get-mailbox?view=exchange-ps">Get-Mailbox</a>.</p>
<div class="language-powershell pre-wrap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$Mailbox</span> <span class="o">=</span> Invoke-Command -Session <span class="nv">$OnlineSession</span> -ScriptBlock <span class="o">{</span> <span class="k">param</span><span class="o">(</span><span class="nv">$user</span><span class="o">)</span> Get-Mailbox -Identity <span class="nv">$user</span> -ErrorAction SilentlyContinue <span class="o">}</span>-ArgumentList <span class="nv">$user</span>
</code></pre></div></div>
<p>Now check the direction and start the mailbox move if it’s not allready there.</p>
<div class="language-powershell pre-wrap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="o">(</span>-not <span class="o">(</span><span class="nv">$MailBox</span><span class="o">)</span> -and <span class="o">(</span><span class="nv">$Direction</span> -eq <span class="s1">'ToCloud'</span><span class="o">))</span> <span class="o">{</span>
<span class="s2">"Migrating To Cloud : </span><span class="nv">$user</span><span class="s2">"</span>
<span class="c1">## Migration to cloud command ##</span>
<span class="o">}</span>
<span class="k">elseif</span> <span class="o">((</span><span class="nv">$MailBox</span><span class="o">)</span> -and <span class="o">(</span><span class="nv">$Direction</span> -eq <span class="s1">'ToExchange'</span><span class="o">))</span> <span class="o">{</span>
<span class="s2">"Migrating To Exchange : </span><span class="nv">$user</span><span class="s2"> to </span><span class="nv">$database</span><span class="s2">"</span>
<span class="c1">## Migration to Exchange comamnd ##</span>
<span class="o">}</span>
<span class="k">else</span> <span class="o">{</span>
<span class="nb">Write-Warning</span> <span class="s2">"Mailbox for </span><span class="nv">$User</span><span class="s2"> already exists Destination : Skiping"</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Migrateing to the cloud the comamnd is fairly strait forward.
I need to pass values to <a href="https://docs.microsoft.com/en-us/powershell/module/exchange/move-and-migration/new-moverequest?view=exchange-ps">New-MoveRequest</a> so I have to Paramaterize the script block and use -ArgumentList.
Format-List is to reduce the amount of data outlput.
I dont need all of it.</p>
<div class="language-powershell pre-wrap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ScriptBlock</span> <span class="o">=</span> <span class="o">{</span>
<span class="k">param</span><span class="o">([</span>PSCredential]<span class="nv">$exchCred</span>, <span class="nv">$user</span><span class="o">)</span>
<span class="nv">$MoveRequestParam</span> <span class="o">=</span> @<span class="o">{</span>
Remote <span class="o">=</span> <span class="nv">$true</span>
RemoteHostName <span class="o">=</span> <span class="s1">'mail.mydomain.com'</span> <span class="c1"># your exchange servers external URL for EWS</span>
RemoteCredential <span class="o">=</span> <span class="nv">$exchCred</span>
TargetDeliveryDomain <span class="o">=</span> <span class="s1">'mydomain.mail.onmicrosoft.com'</span> <span class="c1"># Your *.mail.onmicrosoft.com domain</span>
Identity <span class="o">=</span> <span class="nv">$user</span>
<span class="o">}</span>
New-MoveRequest @MoveRequestParm
<span class="o">}</span>
Invoke-Command -Session <span class="nv">$OnlineSession</span> -ScriptBlock <span class="nv">$ScriptBlock</span> -ArgumentList <span class="nv">$exchCred</span>, <span class="nv">$user</span> | <span class="nb">Format-List </span>DisplayName, QueuedTimestamp
</code></pre></div></div>
<p>Migrateing back to exchange is a little more involved but not by much</p>
<p class="notice--info">If your wondering why I would want to migrate back to exchange. Mistakes do happen, a users that is not ready for migraiton might accendently get moved</p>
<div class="language-powershell pre-wrap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$databaseBaseName</span> <span class="o">=</span> <span class="s1">'db'</span>
<span class="nv">$databaseMaxNumber</span> <span class="o">=</span> 24
<span class="c1">#randomly pick a target DB</span>
<span class="nv">$database</span> <span class="o">=</span> <span class="s2">"</span><span class="nv">$databaseBaseName</span><span class="k">$((</span><span class="nb">get-random</span> <span class="o">-</span>Minimum <span class="m">1</span> <span class="o">-</span>Maximum <span class="nv">$databaseMaxNumber</span><span class="o">)</span>.ToString<span class="o">(</span><span class="s2">"00"</span><span class="k">))</span><span class="s2">"</span>
<span class="s2">"Migrating To Exchange : </span><span class="nv">$user</span><span class="s2"> to </span><span class="nv">$database</span><span class="s2">"</span>
<span class="nv">$scriptBlock</span> <span class="o">=</span> <span class="o">{</span>
<span class="k">param</span><span class="o">([</span>PSCredential]<span class="nv">$exchCred</span>, <span class="nv">$user</span>, <span class="nv">$database</span><span class="o">)</span>
<span class="nv">$MoveRequesParm</span> <span class="o">=</span> @<span class="o">{</span>
Outbound <span class="o">=</span> <span class="nv">$true</span>
RemoteHostName <span class="o">=</span> <span class="s1">'mail.mydomain.com'</span> <span class="c1"># your exchange servers external URL for EWS</span>
RemoteCredential <span class="o">=</span> <span class="nv">$exchCred</span>
TargetDeliveryDomain <span class="o">=</span> <span class="s1">'mydomain.com'</span> <span class="c1"># your onprem email domain</span>
Identity <span class="o">=</span> <span class="nv">$user</span>
RemoteTargetDatabase <span class="o">=</span> <span class="nv">$database</span>
<span class="o">}</span>
New-MoveRequest @MoveRequesParm
<span class="o">}</span>
Invoke-Command -Session <span class="nv">$OnlineSession</span> -ScriptBlock <span class="nv">$scriptblock</span> -ArgumentList <span class="nv">$exchCred</span>, <span class="nv">$user</span>, <span class="nv">$database</span> | <span class="nb">Format-List </span>DisplayName, QueuedTimestamp
</code></pre></div></div>
<p>And there you have the basic’s of the migration. Next up is checking the status.</p>David JonesBladeFireLight@outlook.comNote Some code blocks on this page have word wrap turned on. The problemCollecting CA Certificates for DSC Configuration2017-04-21T00:00:00-05:002017-04-21T00:00:00-05:00https://www.bladefirelight.com/nuggets/collecting-ca-certificates-for-dsc-configuration<h2 id="the-goal">The Goal</h2>
<p>Microsoft PFE Ashely McGlone <a href="https://blogs.msdn.microsoft.com/powershell/2015/10/01/powershell-dsc-faq-sorting-out-certificates/">recommends</a> that each node managed by DSC (Desired State Configuration) have unique certificate for protecting credentials.</p>
<p><img src="https://www.bladefirelight.com/assets/images/MofCredSecurity.png" alt="Mof Credential Security Chart" /></p>
<p class="notice--info">Starting in WMF 5.0 .MOF are <a href="https://msdn.microsoft.com/en-us/powershell/wmf/5.0/dsc_encryptedmof">encrypted at rest</a> on the Node. But that is not the case for the file on the pull server. So I’m going on the assumption that this is still a best practice.</p>
<p>The problem with that is getting the certificates either from the node or from a CA (Certificate Authority) to the DSC development workstation or CI/CD (Continuous Integration/Continuous Deployment) server. I’m going to cover one method.</p>
<h2 id="pre-requisites">Pre-requisites</h2>
<p>For this I’m going to be using <a href="https://github.com/theJasonHelmick/PS-AutoLab-Env">PowerShell Automated Lab Environment</a> and it’s Configuration called “devops-powershell-fundamentals”</p>
<p>This will generate the following:</p>
<ol>
<li>DC1 - Domain Controller and Certificate Authority</li>
<li>S1, S2 - Member servers</li>
<li>N1 - Nano server (not used)</li>
<li>Cli1 - Windows 10 workstation</li>
<li>NAT virtual switch and static IP’s</li>
<li>Certificate Template called “DSC Template”</li>
<li>Auto-Enrollment GPO</li>
</ol>
<p>Once that is up and running we can connect to cli1 via PowerShell Direct</p>
<h2 id="demo">Demo</h2>
<p>I’m going to start with installing PKITools from the PowerShell Gallery (and selecting Yes to install NuGet and allow use of untrusted rpo, this is new lab after all)</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Install-Module PKITools
Get-Command -Module PkiTools
CommandType Name Version Source
----------- ---- ------- ------
<span class="k">Function </span>Get-ADCertificateTemplate 1.6 PkiTools
<span class="k">Function </span>Get-CaLocationString 1.6 PkiTools
<span class="k">Function </span>Get-CertificatAuthority 1.6 PkiTools
<span class="k">Function </span>Get-CertificateTemplateOID 1.6 PkiTools
<span class="k">Function </span>Get-IssuedCertificate 1.6 PkiTools
</code></pre></div></div>
<p>The one I want to focus on is Get-IssuedCertificate. As part of a DSC build process I want to collect the public certificates for each node. Pulling the certificate from each node is one option, but not practical in large network, and may not even be be possible due to lack of connectivity. Thankfully all the public certificates also reside on the Certificate Authority that issues them.</p>
<p>In our lab there is only one domain and one CA (also the DC. no not do that in production) So the code will be simple.</p>
<p>First lets look at the parameters.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SYNTAX
Get-IssuedCertificate <span class="o">[[</span>-ExpireInDays] <Int32>] <span class="o">[[</span>-CAlocation] <<span class="kt">String</span><span class="o">[]>]</span> <span class="o">[[</span>-Properties] <<span class="kt">String</span><span class="o">[]>]</span>
<span class="o">[[</span>-CertificateTemplateOid] <<span class="kt">String</span><span class="o">>]</span> <span class="o">[[</span>-CommonName] <<span class="kt">String</span><span class="o">>]</span> <span class="o">[[</span>-Credential] <PSCredential>] <span class="o">[</span><CommonParameters>]
</code></pre></div></div>
<p>No parameters are required. lets see what it does</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>hv01]: <span class="o">[</span>Cli1]: <span class="nb">PS </span>C:\> Get-IssuedCertificate
Issued Common Name :
Certificate Expiration Date : 4/18/2018 3:40:05 AM
Certificate Effective Date : 4/18/2017 3:40:05 AM
Certificate Template : 1.3.6.1.4.1.311.21.8.8376484.9891361.12404633.14452813.1016466.111.1.29
Issued Request ID : 2
Certificate Hash : f2 4e db 00 12 19 68 79 98 0b 6a 95 47 6d 7a c6 a7 40 e7 63
Request Disposition Message : Issued
Requester Name : COMPANY\DC1<span class="err">$</span>
Binary Certificate : -----BEGIN CERTIFICATE-----
MIIF6jCCBNKgAwIBAgITUQAAAAL5xXyLo72OVAAAAAAAAjANBgkqhkiG9w0BAQsF
ADBZMRAwDgYDVQQKEwdDb21wYW55MRAwDgYDVQQIEwdBcml6b25hMRAwDgYDVQQH
EwdQaG9lbml4MQswCQYDVQQGEwJVUzEUMBIGA1UEAxMLQ29tcGFueS5QcmkwHhcN
MTcwNDE4MDM0MDA1WhcNMTgwNDE4MDM0MDA1WjAAMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAxjtOVDCixBoUEOWG1WKwPqw0DY77upmE0E4Bfc6CV94g
X5ewYtagk7f8KvfigDaoq20qCNzFfaBprseL/0JoKPBY05Ws9O98pfAP639MGP6U
MCbgXZ8ILhtoWZCqHi+S8UVyu7D2meyyl54IgeoVcb0MBISWoGyyJ5tzAWYX1XQL
/0tnQ4YoWRcJulW7Qvn0kUhk6ooe9Mot4gCD0jPE/QjJ/loJDHO+aCRxsMwEil87
KqGKfEcS54dV+g6wRhgxlrfi8b1NDwhKcpgglSzhtmBEUY72m1S5AxuL7V90ZxNU
NQ/CBboy98NKri8RbmyvypAQ47MMyadVVmPLsAgGJwIDAQABo4IDAjCCAv4wNgYJ
KwYBBAGCNxUHBCkwJwYfKwYBBAGCNxUIg/+hJITb3CGF9Y8ZhvKQTb6FEm8BHQIB
cwIBADAUBgNVHSUEDTALBgkrBgEEAYI3FRMwDgYDVR0PAQH/BAQDAgWgMBwGCSsG
AQQBgjcVCgQPMA0wCwYJKwYBBAGCNxUTMHgGCSqGSIb3DQEJDwRrMGkwDgYIKoZI
hvcNAwICAgCAMA4GCCqGSIb3DQMEAgIAgDALBglghkgBZQMEASowCwYJYIZIAWUD
BAEtMAsGCWCGSAFlAwQBAjALBglghkgBZQMEAQUwBwYFKw4DAgcwCgYIKoZIhvcN
AwcwHQYDVR0OBBYEFOB1zbkBT8m9NW7DRh7xW13OjSmLMB8GA1UdIwQYMBaAFA+9
p4ww6yyPs/vD4ugUNRM24pWIMIHFBgNVHR8Egb0wgbowgbeggbSggbGGga5sZGFw
Oi8vL0NOPUNvbXBhbnkuUHJpLENOPURDMSxDTj1DRFAsQ049UHVibGljJTIwS2V5
JTIwU2VydmljZXMsQ049U2VydmljZXMsQ049Q29uZmlndXJhdGlvbixEQz1Db21w
YW55LERDPVByaT9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0P2Jhc2U/b2JqZWN0
Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgb0GCCsGAQUFBwEBBIGwMIGtMIGq
BggrBgEFBQcwAoaBnWxkYXA6Ly8vQ049Q29tcGFueS5QcmksQ049QUlBLENOPVB1
YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZpY2VzLENOPUNvbmZpZ3VyYXRp
b24sREM9Q29tcGFueSxEQz1Qcmk/Y0FDZXJ0aWZpY2F0ZT9iYXNlP29iamVjdENs
YXNzPWNlcnRpZmljYXRpb25BdXRob3JpdHkwPgYDVR0RAQH/BDQwMqAfBgkrBgEE
AYI3GQGgEgQQezARELeYdUOiN2P2QTxNl4IPREMxLkNvbXBhbnkuUHJpMA0GCSqG
SIb3DQEBCwUAA4IBAQBCV12I9eIaFLRFsgzbhMizfufvtCOI/EKAOsEYCxI9HFKJ
H2HfC0lh7eEZABFQKPNx8XgCHCm5skzdR5oHLbtTwrnky2YJkFnuNOgTF/tuunoU
p3+X0Kk3YH88wrv+YPE37PtqF/fJnZvEUzPfxRiaiOFaTisEIXfAxTvVYVBSwK9H
oSgH5GTEblvlmiw6LD7JZYqrmYFCPQdB/mj+aOPjyeFClneGH2u8sxKCZHiJ/RWX
wQJn8DO5ACctnLC8mH70HR5vVRJ0yFX6vrpnRyJEBSpawAYFK1vPf7Wks+gLSw4q
g7ASEJG/MJK0oJpBosOKKGr+jMnEx6BTs1NJ3d0l
-----END CERTIFICATE-----
PSComputerName : DC1.Company.Pri
RunspaceId : 146e5d7a-b85a-4540-9360-fea7413a9978
Issued Common Name :
Certificate Expiration Date : 4/18/2018 3:40:07 AM
Certificate Effective Date : 4/18/2017 3:40:07 AM
Certificate Template : 1.3.6.1.4.1.311.21.8.8376484.9891361.12404633.14452813.1016466.111.1.28
~~~~~~~~SNIP~~~~~~~~~~~~~
BgkrBgEEAYI3UAEwHQYDVR0OBBYEFKYaQhzh28O8Sy4jBG5/63E59OzBMB8GA1Ud
IwQYMBaAFA+9p4ww6yyPs/vD4ugUNRM24pWIMIHFBgNVHR8Egb0wgbowgbeggbSg
gbGGga5sZGFwOi8vL0NOPUNvbXBhbnkuUHJpLENOPURDMSxDTj1DRFAsQ049UHVi
bGljJTIwS2V5JTIwU2VydmljZXMsQ049U2VydmljZXMsQ049Q29uZmlndXJhdGlv
bixEQz1Db21wYW55LERDPVByaT9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0P2Jh
c2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgb0GCCsGAQUFBwEB
BIGwMIGtMIGqBggrBgEFBQcwAoaBnWxkYXA6Ly8vQ049Q29tcGFueS5QcmksQ049
QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZpY2VzLENOPUNv
bmZpZ3VyYXRpb24sREM9Q29tcGFueSxEQz1Qcmk/Y0FDZXJ0aWZpY2F0ZT9iYXNl
P29iamVjdENsYXNzPWNlcnRpZmljYXRpb25BdXRob3JpdHkwGwYDVR0RBBQwEoIQ
Q2xpMS5Db21wYW55LlByaTANBgkqhkiG9w0BAQsFAAOCAQEAFt8fD8dq7WdYxldl
648SS8KkqEeqUv4cBXt5/GBLBO/Cr7JB1m5K8U3VrjzwMgyU4n01h0xeJxYeHIvA
WZ1Fy8BXSzurH3MOcbC0jKuqezzXAJIpVAfkHB0UBKx+OnZes21aGXjb0ZHTv+lv
3lx6Wrc051RD+eDDLz0/+kLU6MRgJHTO8qlgRDRjRSqU7dcU1TKFmpij1IzWjvJb
GU2qHCfAnhWr8b+lMjvRMXkBxQjRF9kmMdhQxhR+nWN0/056wpr7/bukwGzPBKAU
HfFNFXqNT8nc79rB7IW4T6xZteEm0zG/TTNpIjL74/MGDBzFG3MTDE3i6ks9DoF/
<span class="nv">9XDyDg</span><span class="o">==</span>
-----END CERTIFICATE-----
PSComputerName : DC1.Company.Pri
RunspaceId : 146e5d7a-b85a-4540-9360-fea7413a9978
</code></pre></div></div>
<p>That got every issued certificate for every CA on the current domain. More then I need. Obviously we need to trim that down to just the DSC certificates. for that we will need to specify -CertificateTemplateOID. Lets look at the help for that parameter.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>hv01]: <span class="o">[</span>Cli1]: <span class="nb">PS </span>C:\> <span class="nb">help </span>Get-IssuedCertificate -Parameter CertificateTemplateOid
-CertificateTemplateOid <<span class="kt">String</span><span class="o">></span>
<span class="k">Filter </span>on Certificate Template OID <span class="o">(</span>use Get-CertificateTemplateOID<span class="o">)</span>
Required? <span class="nb">false
</span>Position? 4
Default value
Accept pipeline input? <span class="nb">false
</span>Accept wildcard characters? <span class="nb">false</span>
</code></pre></div></div>
<p>Good it has a function to get the OID lets look at that.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SYNTAX
Get-CertificateTemplateOID <span class="o">[</span>-Name] <<span class="kt">String</span><span class="o">></span> <span class="o">[[</span>-Domain] <<span class="kt">String</span><span class="o">>]</span> <span class="o">[</span>-WhatIf] <span class="o">[</span>-Confirm] <span class="o">[</span><CommonParameters>]
</code></pre></div></div>
<p>Only the -Name is required. That I know.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>hv01]: <span class="o">[</span>Cli1]: <span class="nb">PS </span>C:\> Get-CertificateTemplateOID DSCTemplate
1.3.6.1.4.1.311.21.8.16187918.14945684.15749023.11519519.4925321.197.13392998.8282280
</code></pre></div></div>
<p>And I though GUIDS were an eye full.</p>
<p>Get-IssuedCertificate also has a -Propterties paramaters lets look at that.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>hv01]: <span class="o">[</span>Cli1]: <span class="nb">PS </span>C:\> <span class="nb">help </span>Get-IssuedCertificate -Parameter Properties
-Properties <<span class="kt">String</span><span class="o">[]></span>
Fields <span class="k">in </span>the Certificate Authority Database to Export
Required? <span class="nb">false
</span>Position? 3
Default value <span class="o">(</span>
<span class="s1">'Issued Common Name'</span>,
<span class="s1">'Certificate Expiration Date'</span>,
<span class="s1">'Certificate Effective Date'</span>,
<span class="s1">'Certificate Template'</span>,
<span class="c1">#'Issued Email Address',</span>
<span class="s1">'Issued Request ID'</span>,
<span class="s1">'Certificate Hash'</span>,
<span class="c1">#'Request Disposition',</span>
<span class="s1">'Request Disposition Message'</span>,
<span class="s1">'Requester Name'</span>,
<span class="s1">'Binary Certificate'</span> <span class="o">)</span>
Accept pipeline input? <span class="nb">false
</span>Accept wildcard characters? <span class="nb">false</span>
</code></pre></div></div>
<p>For collecting the Certificates I only need the ‘Issued Common Name’ and ‘Binary Certificate’</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>hv01]: <span class="o">[</span>Cli1]: <span class="nb">PS </span>C:\> <span class="nv">$DSCCerts</span> <span class="o">=</span> Get-IssuedCertificate -CertificateTemplateOid <span class="o">(</span>Get-CertificateTemplateOID -Name <span class="s1">'DSC
Template'</span><span class="o">)</span> -Properties <span class="s1">'Issued Common Name'</span>, <span class="s1">'Binary Certificate'</span>
<span class="o">[</span>hv01]: <span class="o">[</span>Cli1]: <span class="nb">PS </span>C:\>
<span class="o">[</span>hv01]: <span class="o">[</span>Cli1]: <span class="nb">PS </span>C:\> <span class="nv">$DSCCerts</span>.Count
4
</code></pre></div></div>
<p>So that got me 4 certs. Now I can step through each one and save them.</p>
<div class="language-powershell pre-wrap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>hv01]: <span class="o">[</span>Cli1]: <span class="nb">PS </span>C:\> <span class="nb">mkdir </span>c:\certs
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 4/21/2017 7:12 AM certs
<span class="o">[</span>hv01]: <span class="o">[</span>Cli1]: <span class="nb">PS </span>C:\> <span class="k">foreach</span> <span class="o">(</span><span class="nv">$cert</span> <span class="k">in</span> <span class="nv">$DSCCerts</span><span class="o">)</span>
<span class="o">>></span> <span class="o">{</span>
<span class="o">>></span> <span class="nb">set-content</span> -path <span class="s2">"c:\certs\</span><span class="k">$(</span><span class="nv">$cert</span>.<span class="s1">'Issued Common Name'</span><span class="k">)</span><span class="s2">.cer"</span> -Value <span class="nv">$cert</span>.<span class="s1">'Binary Certificate'</span> -Encoding Ascii
<span class="o">>></span> <span class="o">}</span>
<span class="o">[</span>hv01]: <span class="o">[</span>Cli1]: <span class="nb">PS </span>C:\> <span class="nb">dir </span>c:\certs
Directory: C:\certs
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 4/21/2017 7:12 AM 1982 Cli1.Company.Pri.cer
-a---- 4/21/2017 7:12 AM 1978 DC1.Company.Pri.cer
-a---- 4/21/2017 7:12 AM 1972 S1.Company.Pri.cer
-a---- 4/21/2017 7:12 AM 1972 S2.Company.Pri.cer
</code></pre></div></div>
<p>Now I have the certificate files for each node I can use for DSC.</p>David JonesBladeFireLight@outlook.comBest practice is to use individual certificates for each Node for DSC. While you can use auto enrollment, getting the certificates to where the MOF are build can be a problem. This is one solutionWindows Image Tools \| Creating an Updated Image Part 32016-10-12T16:48:59-05:002016-10-12T16:48:59-05:00https://www.bladefirelight.com/windowsimagetools/Creating-an-Updateed-Image-Part3-Updates<h2 id="plan-for-today">Plan for today</h2>
<p>The end game is in site. Now that I have 2 images with WMF 5. it’s time to apply all the Windows Updates.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>If you are folowing along you shoudl have a WindowsImageTools Update directory with two images. A striped down Core image, and all you can eat GUI image for source.</p>
<h2 id="kicking-off-updates">Kicking off Updates</h2>
<p>First off I’m going to run the updates. There are a nubmer of ways to do this, Invoke-WindowsUpdate can target individual images or all the images in our Update folders, and it can control the output.
Because i’m doing source and small core images. I will update each image separately, and export the WIM only for the source, and shrink the Core</p>
<p>Lets start with the source</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">get-date
</span>Invoke-WindowsImageUpdate -Path G:\Blog_Example -output Both -ImageName Srv2012r2_Core -ReduceImageSize -Verbose
<span class="nb">get-date
</span>Invoke-WindowsImageUpdate -Path G:\Blog_Example -output WIM -ImageName Srv2012r2_Source -Verbose
<span class="nb">get-date</span>
</code></pre></div></div>
<div class="pre-wrap highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Wednesday, October 12, 2016 12:26:17 AM
VERBOSE: [Invoke-WindowsImageUpdate] : Validateing [Srv2012r2_Core]
VERBOSE: [Invoke-WindowsImageUpdate] : Validateing VM switch config
VERBOSE: Performing the operation "Download required Modules" on target "PowerShell Gallery".
VERBOSE: Loading module from path 'C:\Program Files\WindowsPowerShell\Modules\PackageManagement\1.0.0.1\PackageManagement.psd1'.
VERBOSE: Loading 'FormatsToProcess' from path 'C:\Program Files\WindowsPowerShell\Modules\PackageManagement\1.0.0.1\PackageManagement.format.ps1xml'.
VERBOSE: Loading module from path 'C:\Program Files\WindowsPowerShell\Modules\PackageManagement\1.0.0.1\Microsoft.PowerShell.PackageManagement.dll'.
VERBOSE: Exporting cmdlet 'Find-Package'.
VERBOSE: Exporting cmdlet 'Get-Package'.
VERBOSE: Exporting cmdlet 'Get-PackageProvider'.
VERBOSE: Exporting cmdlet 'Get-PackageSource'.
VERBOSE: Exporting cmdlet 'Install-Package'.
VERBOSE: Exporting cmdlet 'Import-PackageProvider'.
VERBOSE: Exporting cmdlet 'Find-PackageProvider'.
VERBOSE: Exporting cmdlet 'Install-PackageProvider'.
VERBOSE: Exporting cmdlet 'Register-PackageSource'.
VERBOSE: Exporting cmdlet 'Save-Package'.
VERBOSE: Exporting cmdlet 'Set-PackageSource'.
VERBOSE: Exporting cmdlet 'Uninstall-Package'.
VERBOSE: Exporting cmdlet 'Unregister-PackageSource'.
VERBOSE: Importing cmdlet 'Find-Package'.
VERBOSE: Importing cmdlet 'Find-PackageProvider'.
VERBOSE: Importing cmdlet 'Get-Package'.
VERBOSE: Importing cmdlet 'Get-PackageProvider'.
VERBOSE: Importing cmdlet 'Get-PackageSource'.
VERBOSE: Importing cmdlet 'Import-PackageProvider'.
VERBOSE: Importing cmdlet 'Install-Package'.
VERBOSE: Importing cmdlet 'Install-PackageProvider'.
VERBOSE: Importing cmdlet 'Register-PackageSource'.
VERBOSE: Importing cmdlet 'Save-Package'.
VERBOSE: Importing cmdlet 'Set-PackageSource'.
VERBOSE: Importing cmdlet 'Uninstall-Package'.
VERBOSE: Importing cmdlet 'Unregister-PackageSource'.
VERBOSE: [Invoke-WindowsImageUpdate] : Geting latest PSWindowsUpdate
VERBOSE: Using the provider 'PowerShellGet' for searching packages.
VERBOSE: The -Repository parameter was not specified. PowerShellGet will use all of the registered repositories.
VERBOSE: Getting the provider object for the PackageManagement Provider 'NuGet'.
VERBOSE: The specified Location is 'https://www.powershellgallery.com/api/v2/' and PackageManagementProvider is 'NuGet'.
VERBOSE: Searching repository 'https://www.powershellgallery.com/api/v2/FindPackagesById()?id='PSWindowsUpdate'' for ''.
VERBOSE: Total package yield:'1' for the specified package 'PSWindowsUpdate'.
VERBOSE: Performing the operation "Save Package" on target "'PSWindowsUpdate' to location 'G:\Blog_Example\Resource\Modules'".
VERBOSE: The specified module will be installed in 'G:\Blog_Example\Resource\Modules'.
VERBOSE: The specified Location is 'NuGet' and PackageManagementProvider is 'NuGet'.
VERBOSE: Downloading module 'PSWindowsUpdate' with version '1.5.2.2' from the repository 'https://www.powershellgallery.com/api/v2/'.
VERBOSE: Searching repository 'https://www.powershellgallery.com/api/v2/FindPackagesById()?id='PSWindowsUpdate'' for ''.
VERBOSE: InstallPackage' - name='PSWindowsUpdate', version='1.5.2.2',destination='C:\Users\Blade_000\AppData\Local\Temp\239759536'
VERBOSE: DownloadPackage' - name='PSWindowsUpdate', version='1.5.2.2',destination='C:\Users\Blade_000\AppData\Local\Temp\239759536\PSWindowsUpdate\PSWindowsUpdate.nupkg', uri='https://www.powershellgallery.com/api/v2/package/PSWindowsUpdate/1.5.2.2'
VERBOSE: Downloading 'https://www.powershellgallery.com/api/v2/package/PSWindowsUpdate/1.5.2.2'.
VERBOSE: Completed downloading 'https://www.powershellgallery.com/api/v2/package/PSWindowsUpdate/1.5.2.2'.
VERBOSE: Completed downloading 'PSWindowsUpdate'.
VERBOSE: Hash for package 'PSWindowsUpdate' does not match hash provided from the server.
VERBOSE: InstallPackageLocal' - name='PSWindowsUpdate', version='1.5.2.2',destination='C:\Users\Blade_000\AppData\Local\Temp\239759536'
VERBOSE: Module 'PSWindowsUpdate' was saved successfully to path 'G:\Blog_Example\Resource\Modules\PSWindowsUpdate\1.5.2.2'.
VERBOSE: Performing the operation "Invoke Windows Updates on Image" on target "Srv2012r2_Core".
VERBOSE: [Invoke-WindowsImageUpdate] : Windows Update : New Diff Disk : Creating G:\Blog_Example\BaseImage\Srv2012r2_Core_Update.vhdx from G:\Blog_Example\BaseImage\Srv2012r2_Core_base.vhdx
VERBOSE: New-VHD will create a new virtual hard disk with the path "G:\Blog_Example\BaseImage\Srv2012r2_Core_Update.vhdx".
VERBOSE: [Invoke-WindowsImageUpdate] : Windows Update : Adding PSWindowsUpdate Module to G:\Blog_Example\BaseImage\Srv2012r2_Core_Update.vhdx
VERBOSE: [Invoke-WindowsImageUpdate] : Windows Update : updateting AtStartup script
VERBOSE: Sidebyside detected in PSWindowsUpdate : switching to v4 compatability
VERBOSE: [createRunAndWaitVM] : Creating VM 40floaow at 10/12/2016 00:26:34
VERBOSE: [createRunAndWaitVM] : VM 40floaow Stoped
VERBOSE: [createRunAndWaitVM] : VM 40floaow Deleted at 10/12/2016 02:25:36
VERBOSE: [Invoke-WindowsImageUpdate] : Windows Update : Changes detected : Merging G:\Blog_Example\BaseImage\Srv2012r2_Core_Update.vhdx into G:\Blog_Example\BaseImage\Srv2012r2_Core_base.vhdx
VERBOSE: Merge-VHD will merge the virtual hard disk "g:\blog_example\baseimage\srv2012r2_core_update.vhdx" into its parent "g:\blog_example\baseimage\srv2012r2_core_base.vhdx".
Wednesday, October 12, 2016 2:28:08 AM
VERBOSE: [Invoke-WindowsImageUpdate] : Validateing [Srv2012r2_Source]
VERBOSE: [Invoke-WindowsImageUpdate] : Validateing VM switch config
VERBOSE: Performing the operation "Download required Modules" on target "PowerShell Gallery".
VERBOSE: [Invoke-WindowsImageUpdate] : Geting latest PSWindowsUpdate
VERBOSE: Using the provider 'PowerShellGet' for searching packages.
VERBOSE: The -Repository parameter was not specified. PowerShellGet will use all of the registered repositories.
VERBOSE: Getting the provider object for the PackageManagement Provider 'NuGet'.
VERBOSE: The specified Location is 'https://www.powershellgallery.com/api/v2/' and PackageManagementProvider is 'NuGet'.
VERBOSE: Searching repository 'https://www.powershellgallery.com/api/v2/FindPackagesById()?id='PSWindowsUpdate'' for ''.
VERBOSE: Total package yield:'1' for the specified package 'PSWindowsUpdate'.
VERBOSE: Performing the operation "Save Package" on target "'PSWindowsUpdate' to location 'G:\Blog_Example\Resource\Modules'".
VERBOSE: The specified module will be installed in 'G:\Blog_Example\Resource\Modules'.
VERBOSE: The specified Location is 'NuGet' and PackageManagementProvider is 'NuGet'.
VERBOSE: Downloading module 'PSWindowsUpdate' with version '1.5.2.2' from the repository 'https://www.powershellgallery.com/api/v2/'.
VERBOSE: Searching repository 'https://www.powershellgallery.com/api/v2/FindPackagesById()?id='PSWindowsUpdate'' for ''.
VERBOSE: InstallPackage' - name='PSWindowsUpdate', version='1.5.2.2',destination='C:\Users\Blade_000\AppData\Local\Temp\1874556358'
VERBOSE: DownloadPackage' - name='PSWindowsUpdate', version='1.5.2.2',destination='C:\Users\Blade_000\AppData\Local\Temp\1874556358\PSWindowsUpdate\PSWindowsUpdate.nupkg', uri='https://www.powershellgallery.com/api/v2/package/PSWindowsUpdate/1.5.2.2'
VERBOSE: Downloading 'https://www.powershellgallery.com/api/v2/package/PSWindowsUpdate/1.5.2.2'.
VERBOSE: Completed downloading 'https://www.powershellgallery.com/api/v2/package/PSWindowsUpdate/1.5.2.2'.
VERBOSE: Completed downloading 'PSWindowsUpdate'.
VERBOSE: Hash for package 'PSWindowsUpdate' does not match hash provided from the server.
VERBOSE: InstallPackageLocal' - name='PSWindowsUpdate', version='1.5.2.2',destination='C:\Users\Blade_000\AppData\Local\Temp\1874556358'
VERBOSE: Module 'PSWindowsUpdate' was saved successfully to path 'G:\Blog_Example\Resource\Modules\PSWindowsUpdate\1.5.2.2'.
VERBOSE: Performing the operation "Invoke Windows Updates on Image" on target "Srv2012r2_Source".
VERBOSE: [Invoke-WindowsImageUpdate] : Windows Update : New Diff Disk : Creating G:\Blog_Example\BaseImage\Srv2012r2_Source_Update.vhdx from G:\Blog_Example\BaseImage\Srv2012r2_Source_base.vhdx
VERBOSE: New-VHD will create a new virtual hard disk with the path "G:\Blog_Example\BaseImage\Srv2012r2_Source_Update.vhdx".
VERBOSE: [Invoke-WindowsImageUpdate] : Windows Update : Adding PSWindowsUpdate Module to G:\Blog_Example\BaseImage\Srv2012r2_Source_Update.vhdx
VERBOSE: [Invoke-WindowsImageUpdate] : Windows Update : updateting AtStartup script
VERBOSE: Sidebyside detected in PSWindowsUpdate : switching to v4 compatability
VERBOSE: [createRunAndWaitVM] : Creating VM 5dvm2bu2 at 10/12/2016 02:28:24
VERBOSE: [createRunAndWaitVM] : VM 5dvm2bu2 Stoped
VERBOSE: [createRunAndWaitVM] : VM 5dvm2bu2 Deleted at 10/12/2016 05:30:56
VERBOSE: [Invoke-WindowsImageUpdate] : Windows Update : Changes detected : Merging G:\Blog_Example\BaseImage\Srv2012r2_Source_Update.vhdx into G:\Blog_Example\BaseImage\Srv2012r2_Source_base.vhdx
VERBOSE: Merge-VHD will merge the virtual hard disk "g:\blog_example\baseimage\srv2012r2_source_update.vhdx" into its parent "g:\blog_example\baseimage\srv2012r2_source_base.vhdx".
VERBOSE: [Invoke-WindowsImageUpdate] : SysPrep : New Diff Disk : Creating G:\Blog_Example\BaseImage\Srv2012r2_Source_Sysprep.vhdx from G:\Blog_Example\BaseImage\Srv2012r2_Source_base.vhdx
VERBOSE: New-VHD will create a new virtual hard disk with the path "G:\Blog_Example\BaseImage\Srv2012r2_Source_Sysprep.vhdx".
VERBOSE: [Invoke-WindowsImageUpdate] : SysPrep : updateting AtStartup script
VERBOSE: [Invoke-WindowsImageUpdate] : SysPrep : Creating temp vm and waiting
VERBOSE: [createRunAndWaitVM] : Creating VM 4nxvrw5x at 10/12/2016 05:34:45
VERBOSE: [createRunAndWaitVM] : VM 4nxvrw5x Stoped
VERBOSE: [createRunAndWaitVM] : VM 4nxvrw5x Deleted at 10/12/2016 05:41:37
VERBOSE: [Invoke-WindowsImageUpdate] : SysPrep : Removing PageFile and PsTemp
VERBOSE: [Invoke-WindowsImageUpdate] : SysPrep : Cleaning SxS
VERBOSE: [Invoke-WindowsImageUpdate] : WIM : Creating G:\Blog_Example\UpdatedImageShare\Srv2012r2_Source.wim
VERBOSE: [Invoke-WindowsImageUpdate] : WIM : removing G:\Blog_Example\BaseImage\Srv2012r2_Source_Sysprep.vhdx
Wednesday, October 12, 2016 6:19:52 AM
</code></pre></div></div>
<p>After a few hours I have a a fully patched system.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>G:\Blog_Example\UpdatedImageShare
Directory: G:\Blog_Example\UpdatedImageShare
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 9/27/2016 3:24 PM 7541358592 Srv2012r2_Core.vhdx
-a---- 9/27/2016 3:14 PM 3415926982 Srv2012r2_Core.wim
-a---- 9/26/2016 6:36 PM 4523302203 Srv2012r2_Source.wim
</code></pre></div></div>David JonesBladeFireLight@outlook.comOne of the time consuming steps to deploying new VMs is the time spend managing Images and and applying patches. I’m not big on Golden images. I tend to use a fully patched VHDX or VMDK and let DSC handle the configuration and software. This is not the fastest, and at scale you need to create more then one image based on what saves the most time. (IIS, SQL, Exchange, etc…).Windows Image Tools \| Creating an Updated Image Part 22016-09-01T16:48:59-05:002016-09-01T16:48:59-05:00https://www.bladefirelight.com/windowsimagetools/Creating-an-Updateed-Image-Part2-WMF5<h2 id="plan-for-today">Plan for today</h2>
<p>Windows Management Framework contains PowerShell. You should always run the latest supported in your environment, and it’s a good idea to get your hands on the preview and give it test run. I want my 2012r2 servers to be running WMF 5.0 the current stable realease.</p>
<p>Now I could install 5.1 preview, but as it has to be uninstalled when the final get’s released, so I’m going to pass for now.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>If you folowing along you will need the WindowsImageTools update folder and two VHDX created in the last blog</p>
<h2 id="updating-wmf">Updating WMF</h2>
<p>Using Update-WindowsImageWMF we will download WMF 5.0 and .net 4.6.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">PS </span>G:\> Update-WindowsImageWMF -Path G:\Blog_Example\ -ImageName Srv2012r2_Core -Verbose
</code></pre></div></div>
<p>First it’s going to create a child vhdx so we can revert if things go south</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>VERBOSE: Performing the operation <span class="s2">"Update WMF in Windows Image Tools Update Image"</span> on target
<span class="s2">"G:\Blog_Example\\BaseImage\Srv2012r2_Core_Base.vhdx"</span><span class="nb">.</span>
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : Creating G:\Blog_Example\\BaseImage\Srv2012r2_Core_Update.vhdx <span class="k">from
</span>G:\Blog_Example\\BaseImage\Srv2012r2_Core_Base.vhdx
</code></pre></div></div>
<p>Then it’s going to download the installer packages</p>
<div class="pre-wrap highlighter-rouge"><div class="highlight"><pre class="highlight"><code>VERBOSE: [Update-WindowsImageWMF] : Checking for the latest WMF in G:\Blog_Example\\Resource\WMF\5
VERBOSE: GET https://www.microsoft.com/en-us/download/details.aspx?id=50395 with 0-byte payload
VERBOSE: received 107311-byte response of content type text/html
VERBOSE: GET http://www.microsoft.com/en-us/download/confirmation.aspx?id=50395 with 0-byte payload
VERBOSE: received -1-byte response of content type text/html
WARNING: [Update-WindowsImageWMF] : Checking for the latest WMF : W2K12-KB3134759-x64.msu Missing, Downloading
VERBOSE: GET https://download.microsoft.com/download/2/C/6/2C6E1B4A-EBE5-48A6-B225-2D2058A9CEFB/W2K12-KB3134759-x64.msu
with 0-byte payload
VERBOSE: received 21540661-byte response of content type application/octet-stream
VERBOSE: [Update-WindowsImageWMF] : Checking for the latest WMF :
G:\Blog_Example\\Resource\WMF\5\W2K12-KB3134759-x64.msu : Found
WARNING: [Update-WindowsImageWMF] : Checking for the latest WMF : Win7AndW2K8R2-KB3134760-x64.msu Missing, Downloading
VERBOSE: GET
https://download.microsoft.com/download/2/C/6/2C6E1B4A-EBE5-48A6-B225-2D2058A9CEFB/Win7AndW2K8R2-KB3134760-x64.msu with
0-byte payload
VERBOSE: received 21779572-byte response of content type application/octet-stream
VERBOSE: [Update-WindowsImageWMF] : Checking for the latest WMF :
G:\Blog_Example\\Resource\WMF\5\Win7AndW2K8R2-KB3134760-x64.msu : Found
WARNING: [Update-WindowsImageWMF] : Checking for the latest WMF : Win7-KB3134760-x86.msu Missing, Downloading
VERBOSE: GET https://download.microsoft.com/download/2/C/6/2C6E1B4A-EBE5-48A6-B225-2D2058A9CEFB/Win7-KB3134760-x86.msu
with 0-byte payload
VERBOSE: received 16961221-byte response of content type application/octet-stream
VERBOSE: [Update-WindowsImageWMF] : Checking for the latest WMF :
G:\Blog_Example\\Resource\WMF\5\Win7-KB3134760-x86.msu : Found
WARNING: [Update-WindowsImageWMF] : Checking for the latest WMF : Win8.1AndW2K12R2-KB3134758-x64.msu Missing,
Downloading
VERBOSE: GET
https://download.microsoft.com/download/2/C/6/2C6E1B4A-EBE5-48A6-B225-2D2058A9CEFB/Win8.1AndW2K12R2-KB3134758-x64.msu
with 0-byte payload
VERBOSE: received 19764832-byte response of content type application/octet-stream
VERBOSE: [Update-WindowsImageWMF] : Checking for the latest WMF :
G:\Blog_Example\\Resource\WMF\5\Win8.1AndW2K12R2-KB3134758-x64.msu : Found
WARNING: [Update-WindowsImageWMF] : Checking for the latest WMF : Win8.1-KB3134758-x86.msu Missing, Downloading
VERBOSE: GET
https://download.microsoft.com/download/2/C/6/2C6E1B4A-EBE5-48A6-B225-2D2058A9CEFB/Win8.1-KB3134758-x86.msu with 0-byte
payload
VERBOSE: received 15059790-byte response of content type application/octet-stream
VERBOSE: [Update-WindowsImageWMF] : Checking for the latest WMF :
G:\Blog_Example\\Resource\WMF\5\Win8.1-KB3134758-x86.msu : Found
VERBOSE: [Update-WindowsImageWMF] : Checking for .NET 4.6.1 installer
WARNING: [Update-WindowsImageWMF] : Checking for .NET 4.6 installer : Missing : Downloading
VERBOSE: GET
https://download.microsoft.com/download/E/4/1/E4173890-A24A-4936-9FC9-AF930FE3FA40/NDP461-KB3102436-x86-x64-AllOS-ENU.e
xe with 0-byte payload
VERBOSE: received 67681000-byte response of content type application/octet-stream
</code></pre></div></div>
<p>Now it will copy the .NET installer and an AtStartup.ps1 to run the install.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : .NET : Adding installer to G:\Blog_Example\\BaseImage\Srv2012r2_Core_Update.vhdx
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : .NET : updateting AtStartup script
</code></pre></div></div>
<p>It is going to create a VM with a random name and wait for it to stop.
While it’s running the AtStartup script will install .net, reboot and check the version of .net
If it find’s what it’s expecting it creates a file that Update-WindowsImageWMF will look for to decide if it can continue.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : .NET : Creating temp vm and waiting
VERBOSE: <span class="o">[</span>createRunAndWaitVM] : Creating VM s4gzlpa4 at 09/22/2016 16:54:47
VERBOSE: <span class="o">[</span>createRunAndWaitVM] : VM s4gzlpa4 Stoped
VERBOSE: <span class="o">[</span>createRunAndWaitVM] : VM s4gzlpa4 Deleted at 09/22/2016 17:12:52
</code></pre></div></div>
<p>You cant use PowerShell to WMF because that updates PowerShell. So It will apply the MSU to the VHDX.
Now because Update-WindowsImageWMF does not know what OS is inside the vhdx. It will apply them all. Unfortunetly DISM (that does the work) reports succses when the MSU does not apply to that version of the OS. So it looks like all of the are installing but only the one that should actualy does.</p>
<div class="language-powershell pre-wrap highlighter-rouge"><div class="highlight"><pre class="highlight"><code>VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : WMF : Applying WMF to G:\Blog_Example\\BaseImage\Srv2012r2_Core_Update.vhdx and
Updating AtStartup script
VERBOSE: checking <span class="k">if </span>G:\Blog_Example\Resource\WMF\5\W2K12-KB3134759-x64.msu applies to
G:\Blog_Example\\BaseImage\Srv2012r2_Core_Update.vhdx
VERBOSE: Target Image Version 6.3.9600.17031
VERBOSE: Successfully added package G:\Blog_Example\Resource\WMF\5\W2K12-KB3134759-x64.msu
VERBOSE: checking <span class="k">if </span>G:\Blog_Example\Resource\WMF\5\Win7-KB3134760-x86.msu applies to
G:\Blog_Example\\BaseImage\Srv2012r2_Core_Update.vhdx
VERBOSE: Target Image Version 6.3.9600.17031
VERBOSE: Successfully added package G:\Blog_Example\Resource\WMF\5\Win7-KB3134760-x86.msu
VERBOSE: checking <span class="k">if </span>G:\Blog_Example\Resource\WMF\5\Win7AndW2K8R2-KB3134760-x64.msu applies to
G:\Blog_Example\\BaseImage\Srv2012r2_Core_Update.vhdx
VERBOSE: Target Image Version 6.3.9600.17031
VERBOSE: Successfully added package G:\Blog_Example\Resource\WMF\5\Win7AndW2K8R2-KB3134760-x64.msu
VERBOSE: checking <span class="k">if </span>G:\Blog_Example\Resource\WMF\5\Win8.1-KB3134758-x86.msu applies to
G:\Blog_Example\\BaseImage\Srv2012r2_Core_Update.vhdx
VERBOSE: Target Image Version 6.3.9600.17031
VERBOSE: Successfully added package G:\Blog_Example\Resource\WMF\5\Win8.1-KB3134758-x86.msu
VERBOSE: checking <span class="k">if </span>G:\Blog_Example\Resource\WMF\5\Win8.1AndW2K12R2-KB3134758-x64.msu applies to
G:\Blog_Example\\BaseImage\Srv2012r2_Core_Update.vhdx
VERBOSE: Target Image Version 6.3.9600.17031
VERBOSE: Successfully added package G:\Blog_Example\Resource\WMF\5\Win8.1AndW2K12R2-KB3134758-x64.msu
</code></pre></div></div>
<p>To finalize the install Update-WindowsImageWMF needs to create a vm and let it finish installing then run a AtStartup script to check the version of PowerShell to validate the install worked. and if so write a flag file.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : WMF : creating temp VM to finalize <span class="nb">install </span>on
G:\Blog_Example\\BaseImage\Srv2012r2_Core_Update.vhdx
VERBOSE: <span class="o">[</span>createRunAndWaitVM] : Creating VM yary1ogs at 09/22/2016 17:15:55
VERBOSE: <span class="o">[</span>createRunAndWaitVM] : VM yary1ogs Stoped
VERBOSE: <span class="o">[</span>createRunAndWaitVM] : VM yary1ogs Deleted at 09/22/2016 17:18:17
</code></pre></div></div>
<p>If the flag file is detected then the child vhdx is merged back into the base. if not the child file id discarded and an error thrown</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : WMF : Checking <span class="k">if </span>changes made
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : WMF : Changes found : Merging G:\Blog_Example\\BaseImage\Srv2012r2_Core_Update.vhdx
into G:\Blog_Example\\BaseImage\Srv2012r2_Core_Base.vhdx
</code></pre></div></div>
<p>I’m going to run the same command again on the other vhdx</p>
<div class="language-powershell pre-wrap highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Update-WindowsImageWMF -Path G:\Blog_Example\ -ImageName Srv2012r2_Source -Verbose
VERBOSE: Performing the operation <span class="s2">"Update WMF in Windows Image Tools Update Image"</span> on target <span class="s2">"G:\Blog_Example\\BaseImage\Srv2012r2_Source_Base.vhdx"</span><span class="nb">.</span>
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : Creating G:\Blog_Example\\BaseImage\Srv2012r2_Source_Update.vhdx <span class="k">from </span>G:\Blog_Example\\BaseImage\Srv2012r2_Source_Base.vhdx
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : Checking <span class="k">for </span>the latest WMF <span class="k">in </span>G:\Blog_Example\\Resource\WMF\5
VERBOSE: GET https://www.microsoft.com/en-us/download/details.aspx?id<span class="o">=</span>50395 with 0-byte payload
VERBOSE: received 107378-byte response of content <span class="nb">type </span>text/html
VERBOSE: GET http://www.microsoft.com/en-us/download/confirmation.aspx?id<span class="o">=</span>50395 with 0-byte payload
VERBOSE: received -1-byte response of content <span class="nb">type </span>text/html
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : Checking <span class="k">for </span>the latest WMF : G:\Blog_Example\\Resource\WMF\5\W2K12-KB3134759-x64.msu : Found
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : Checking <span class="k">for </span>the latest WMF : G:\Blog_Example\\Resource\WMF\5\W2K12-KB3134759-x64.msu : Found
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : Checking <span class="k">for </span>the latest WMF : G:\Blog_Example\\Resource\WMF\5\Win7AndW2K8R2-KB3134760-x64.msu : Found
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : Checking <span class="k">for </span>the latest WMF : G:\Blog_Example\\Resource\WMF\5\Win7AndW2K8R2-KB3134760-x64.msu : Found
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : Checking <span class="k">for </span>the latest WMF : G:\Blog_Example\\Resource\WMF\5\Win7-KB3134760-x86.msu : Found
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : Checking <span class="k">for </span>the latest WMF : G:\Blog_Example\\Resource\WMF\5\Win7-KB3134760-x86.msu : Found
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : Checking <span class="k">for </span>the latest WMF : G:\Blog_Example\\Resource\WMF\5\Win8.1AndW2K12R2-KB3134758-x64.msu : Found
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : Checking <span class="k">for </span>the latest WMF : G:\Blog_Example\\Resource\WMF\5\Win8.1AndW2K12R2-KB3134758-x64.msu : Found
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : Checking <span class="k">for </span>the latest WMF : G:\Blog_Example\\Resource\WMF\5\Win8.1-KB3134758-x86.msu : Found
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : Checking <span class="k">for </span>the latest WMF : G:\Blog_Example\\Resource\WMF\5\Win8.1-KB3134758-x86.msu : Found
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : Checking <span class="k">for</span> .NET 4.6.1 installer
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : .NET : Adding installer to G:\Blog_Example\\BaseImage\Srv2012r2_Source_Update.vhdx
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : .NET : updateting AtStartup script
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : .NET : Creating temp vm and waiting
VERBOSE: <span class="o">[</span>createRunAndWaitVM] : Creating VM dkdjxvqm at 09/23/2016 08:56:23
VERBOSE: <span class="o">[</span>createRunAndWaitVM] : VM dkdjxvqm Stoped
VERBOSE: <span class="o">[</span>createRunAndWaitVM] : VM dkdjxvqm Deleted at 09/23/2016 09:12:39
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : WMF : Applying WMF to G:\Blog_Example\\BaseImage\Srv2012r2_Source_Update.vhdx and Updating AtStartup script
VERBOSE: checking <span class="k">if </span>G:\Blog_Example\Resource\WMF\5\W2K12-KB3134759-x64.msu applies to G:\Blog_Example\\BaseImage\Srv2012r2_Source_Update.vhdx
VERBOSE: Target Image Version 6.3.9600.17031
VERBOSE: Successfully added package G:\Blog_Example\Resource\WMF\5\W2K12-KB3134759-x64.msu
VERBOSE: checking <span class="k">if </span>G:\Blog_Example\Resource\WMF\5\Win7-KB3134760-x86.msu applies to G:\Blog_Example\\BaseImage\Srv2012r2_Source_Update.vhdx
VERBOSE: Target Image Version 6.3.9600.17031
VERBOSE: Successfully added package G:\Blog_Example\Resource\WMF\5\Win7-KB3134760-x86.msu
VERBOSE: checking <span class="k">if </span>G:\Blog_Example\Resource\WMF\5\Win7AndW2K8R2-KB3134760-x64.msu applies to G:\Blog_Example\\BaseImage\Srv2012r2_Source_Update.vhdx
VERBOSE: Target Image Version 6.3.9600.17031
VERBOSE: Successfully added package G:\Blog_Example\Resource\WMF\5\Win7AndW2K8R2-KB3134760-x64.msu
VERBOSE: checking <span class="k">if </span>G:\Blog_Example\Resource\WMF\5\Win8.1-KB3134758-x86.msu applies to G:\Blog_Example\\BaseImage\Srv2012r2_Source_Update.vhdx
VERBOSE: Target Image Version 6.3.9600.17031
VERBOSE: Successfully added package G:\Blog_Example\Resource\WMF\5\Win8.1-KB3134758-x86.msu
VERBOSE: checking <span class="k">if </span>G:\Blog_Example\Resource\WMF\5\Win8.1AndW2K12R2-KB3134758-x64.msu applies to G:\Blog_Example\\BaseImage\Srv2012r2_Source_Update.vhdx
VERBOSE: Target Image Version 6.3.9600.17031
VERBOSE: Successfully added package G:\Blog_Example\Resource\WMF\5\Win8.1AndW2K12R2-KB3134758-x64.msu
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : WMF : creating temp VM to finalize <span class="nb">install </span>on G:\Blog_Example\\BaseImage\Srv2012r2_Source_Update.vhdx
VERBOSE: <span class="o">[</span>createRunAndWaitVM] : Creating VM e5h2cc34 at 09/23/2016 09:15:50
VERBOSE: <span class="o">[</span>createRunAndWaitVM] : VM e5h2cc34 Stoped
VERBOSE: <span class="o">[</span>createRunAndWaitVM] : VM e5h2cc34 Deleted at 09/23/2016 09:18:25
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : WMF : Checking <span class="k">if </span>changes made
VERBOSE: <span class="o">[</span>Update-WindowsImageWMF] : WMF : Changes found : Merging G:\Blog_Example\\BaseImage\Srv2012r2_Source_Update.vhdx into G:\Blog_Example\\BaseImage\Srv2012r2_Source_Base.vhdx
</code></pre></div></div>
<p>Now I’m going to quickly check to make sure this worked. I dont want to make any changes to the drive as it is so I will create a child vhdx and boot it.</p>
<div class="language-powershell pre-wrap highlighter-rouge"><div class="highlight"><pre class="highlight"><code>New-VHD -Path G:\Blog_Example\BaseImage\Srv2012r2_Core_temp.vhdx -ParentPath G:\Blog_Example\BaseImage\Srv2012r2_Core_base.vhdx
Invoke-CreateVmRunAndWait -VhdPath G:\Blog_Example\\BaseImage\Srv2012r2_Core_temp.vhdx
<span class="nb">Remove-Item </span>G:\Blog_Example\\BaseImage\Srv2012r2_Core_temp.vhdx
</code></pre></div></div>
<p>checking the psversion table we see what is expected.</p>
<p><img src="https://www.bladefirelight.com\assets\images\PS5Version.JPG" alt="PowerShell Version Table" /></p>
<p class="notice--info"><strong>Dealing with 2008/Win7</strong> WMF 5 on 2008 and Win7 need a few patches after WMF 4 is installed. So You want to run windows update before installing WMF 5</p>
<p>Next up. Windows update and output of WIM/VHDX</p>David JonesBladeFireLight@outlook.comOne of the time consuming steps to deploying new VMs is the time spend managing Images and and applying patches. I’m not big on Golden images. I tend to use a fully patched VHDX or VMDK and let DSC handle the configuration and software. This is not the fastest, and at scale you need to create more then one image based on what saves the most time. (IIS, SQL, Exchange, etc…).Windows Image Tools \| Creating an Updated Image Part 12016-09-01T16:48:59-05:002016-09-01T16:48:59-05:00https://www.bladefirelight.com/windowsimagetools/Creating-an-Updateed-Image-Part1-Base-VHDX<h2 id="staring-over">Staring over</h2>
<p>In the last post I demonstrated creating a fresh VHDX, with unattent.xml and using that to install 7zip.</p>
<p>Now i’m going to throw all that out, and starting over with a base image to install WMF5 and run windows updates.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>For this we need the folder scructure used the WindowsImageTools to store the files. Luckily we have New-WindowsImageToolsExample that will create the folders and files we need.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>New-WindowsImageToolsExample -Path g:\Blog_Example
WARNING: Unable to <span class="nb">read </span>Windows Image Tools Update Cofniguration <span class="k">from </span>g:\Blog_Example\Config.xml, creating a new file
Directory: G:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 9/6/2016 4:09 PM Blog_Example
</code></pre></div></div>
<p>Let’s take a look at what we get</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dir G:\Blog_Example\
Directory: G:\Blog_Example
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 9/6/2016 4:09 PM BaseImage
d----- 9/6/2016 4:09 PM ISO
d----- 9/6/2016 4:09 PM Resource
d----- 9/6/2016 4:09 PM UpdatedImageShare
-a---- 9/6/2016 4:09 PM 3993 AdvancedUpdateExample.ps1
-a---- 9/6/2016 4:09 PM 4775 BasicConvertExample.ps1
-a---- 9/6/2016 4:09 PM 7456 BasicUpdateExample.ps1
-a---- 9/6/2016 4:09 PM 1788 Config.xml
-a---- 9/6/2016 4:09 PM 4814 DownloadEvalIso.ps1
</code></pre></div></div>
<p>I will not be delving into the example ps1 files. I’m going to walkthrough a similar example to the advanced one.</p>
<p>We will start by looking at the Config.xml</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><Objs</span> <span class="na">Version=</span><span class="s">"1.1.0.1"</span> <span class="na">xmlns=</span><span class="s">"http://schemas.microsoft.com/powershell/2004/04"</span><span class="nt">></span>
<span class="nt"><Obj</span> <span class="na">RefId=</span><span class="s">"0"</span><span class="nt">></span>
<span class="nt"><TN</span> <span class="na">RefId=</span><span class="s">"0"</span><span class="nt">></span>
<span class="nt"><T></span>System.Collections.Hashtable<span class="nt"></T></span>
<span class="nt"><T></span>System.Object<span class="nt"></T></span>
<span class="nt"></TN></span>
<span class="nt"><DCT></span>
<span class="nt"><En></span>
<span class="nt"><S</span> <span class="na">N=</span><span class="s">"Key"</span><span class="nt">></span>Gateway<span class="nt"></S></span>
<span class="nt"><S</span> <span class="na">N=</span><span class="s">"Value"</span><span class="nt">></span>192.168.0.1<span class="nt"></S></span>
<span class="nt"></En></span>
<span class="nt"><En></span>
<span class="nt"><S</span> <span class="na">N=</span><span class="s">"Key"</span><span class="nt">></span>vLan<span class="nt"></S></span>
<span class="nt"><I32</span> <span class="na">N=</span><span class="s">"Value"</span><span class="nt">></span>0<span class="nt"></I32></span>
<span class="nt"></En></span>
<span class="nt"><En></span>
<span class="nt"><S</span> <span class="na">N=</span><span class="s">"Key"</span><span class="nt">></span>IpAddress<span class="nt"></S></span>
<span class="nt"><S</span> <span class="na">N=</span><span class="s">"Value"</span><span class="nt">></span>192.168.0.100<span class="nt"></S></span>
<span class="nt"></En></span>
<span class="nt"><En></span>
<span class="nt"><S</span> <span class="na">N=</span><span class="s">"Key"</span><span class="nt">></span>VmSwitch<span class="nt"></S></span>
<span class="nt"><S</span> <span class="na">N=</span><span class="s">"Value"</span><span class="nt">></span>vmswitch<span class="nt"></S></span>
<span class="nt"></En></span>
<span class="nt"><En></span>
<span class="nt"><S</span> <span class="na">N=</span><span class="s">"Key"</span><span class="nt">></span>IpType<span class="nt"></S></span>
<span class="nt"><S</span> <span class="na">N=</span><span class="s">"Value"</span><span class="nt">></span>DHCP<span class="nt"></S></span>
<span class="nt"></En></span>
<span class="nt"><En></span>
<span class="nt"><S</span> <span class="na">N=</span><span class="s">"Key"</span><span class="nt">></span>SubnetMask<span class="nt"></S></span>
<span class="nt"><I32</span> <span class="na">N=</span><span class="s">"Value"</span><span class="nt">></span>24<span class="nt"></I32></span>
<span class="nt"></En></span>
<span class="nt"><En></span>
<span class="nt"><S</span> <span class="na">N=</span><span class="s">"Key"</span><span class="nt">></span>DnsServer<span class="nt"></S></span>
<span class="nt"><S</span> <span class="na">N=</span><span class="s">"Value"</span><span class="nt">></span>192.168.0.1<span class="nt"></S></span>
<span class="nt"></En></span>
<span class="nt"></DCT></span>
<span class="nt"></Obj></span>
<span class="nt"></Objs></span>
</code></pre></div></div>
<p>It’s a basic clixml file created with Export-Clixml containing the configuration used by WindowsImageTools to manage network settings for the vm’s it creates. Now I’m not about to edit that file manual, and while I could use Import-Clixml/Export-Clixml to work with it. It simpler to use the functions that come with WindowsImageTools Get-UpdateConfig/Set-UpdateConfig</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Get-UpdateConfig -Path G:\Blog_Example\
Name Value
---- -----
IpType DHCP
vLan 0
DnsServer 192.168.0.1
Gateway 192.168.0.1
VmSwitch vmswitch
SubnetMask 24
IpAddress 192.168.0.100
</code></pre></div></div>
<p>As you can see the default uses DHCP for the vm’s IP assignment, does not use vLan tageing and attached to the virtual switch called vmswitch. To use it in my enviroment I need to make one small change</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">Set</span>-UpdateConfig -Path G:\Blog_Example\ -VmSwitch Bridge | Get-UpdateConfig
Name Value
---- -----
IpType DHCP
vLan 0
DnsServer 192.168.0.1
Gateway 192.168.0.1
VmSwitch Bridge
SubnetMask 24
IpAddress 192.168.0.100
</code></pre></div></div>
<p>Now we are ready to add a base images.</p>
<h2 id="a-la-carte-or-all-you-can-eat">A la carte or All you can eat</h2>
<p>I’m going to create two images. One bear-bones 2012 R2 Core and one 2012 R2 GUI with .net 3.5. The reason for this is I want one really small WIM/VHDX for a starting port, and one WIM that I can use for a source to add Windows Features.</p>
<p class="notice--info"><strong>Why the Soruce WIM?</strong> Windows Core comes with very little of the Windows Features installed, and when you add them Install-WindowsFeature will look to Windows Update for the missing bits. However if you don’t have internet access then you need a source WIM. In a secure environment InfoSec usually frowns on web surfing from servers and expect you to use WSUS for updates. Unfortunately Install-WindowsFeature will not use WSUS.</p>
<pre><code class="language-PowerShell">Add-UpdateImage -Path 'G:\Blog_Example\' -FriendlyName 'Srv2012r2_Source' -DiskLayout UEFI -SourcePath 'G:\iso\Srv2012r2Eval.ISO' -AdminCredential (Get-Credential) -AddPayloadForRemovedFeature -Index 4
Add-UpdateImage -Path 'G:\Blog_Example\' -FriendlyName 'Srv2012r2_Core' -DiskLayout UEFI -SourcePath 'G:\iso\Srv2012r2Eval.ISO' -AdminCredential (Get-Credential) -Index 3
dir 'G:\Blog_Example\BaseImage\'
</code></pre>
<p>This gives us the two files.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Directory: G:\Blog_Example\BaseImage
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 9/6/2016 8:55 PM 4874829824 Srv2012r2_Core_base.vhdx
-a---- 9/6/2016 8:28 PM 8250195968 Srv2012r2_Source_base.vhdx
</code></pre></div></div>
<p>Next up I’m going to update both to WMF5.</p>David JonesBladeFireLight@outlook.comOne of the time consuming steps to deploying new VMs is the time spend managing Images and and applying patches. I’m not big on Golden images. I tend to use a fully patched VHDX or VMDK and let DSC handle the configuration and software. This is not the fastest, and at scale you need to create more then one image based on what saves the most time. (IIS, SQL, Exchange, etc…).Re-factoring the Blog2016-09-01T00:00:00-05:002016-09-01T00:00:00-05:00https://www.bladefirelight.com/Blog-Refactoring<h2 id="sometimes-rebooting-fixes-everything">Sometimes rebooting fixes everything</h2>
<p>After spending month after month on WidnowsImageTools and little else in my free time. My blog has been looking like abandonware. I had always planed to comeback, but there was always this nagging issue. WordPress. is a hosted service and cost money if you want more then the vanilla. And I had no interest in hosting WordPress elseware. It also was missing a plug-in that allowed you to to reference GitHub files directly. Copying code from a GitHub project into Gist seems like a major waste of time.</p>
<h2 id="enter-jekyll">Enter Jekyll</h2>
<p>A few months ago I listened to a podcast where they briefly mentioned github pages. This perked my interest. Partially because if was free, and partly because if was GitHub. (I also like the idea of bloging like a hacker).</p>
<p>I figured if it was hosted on GitHub, surely they would have a easy way to reference a GitHub Repo. No such luck. The default plugins were somewhat limited. If I wanted unsupported plugins I had to compile the site locally. So I looked at all the plugins for Jekyll in Ruby Gems, I came across one that worked. However it only wanted to work in Jekyll version 1. I looked at the code on it’s GitHub repo and it looked to be an arbitrary requirement. So I set out the setup Jekyll using Bash on Windows 10.</p>
<p>I plan to write a full blog on the experience. There were many bumps and gatcha’s getting here, but I now can reference sections of GitHub Repos files in my posts, so it was worth it.</p>David JonesBladeFireLight@outlook.comSometimes rebooting fixes everything After spending month after month on WidnowsImageTools and little else in my free time. My blog has been looking like abandonware. I had always planed to comeback, but there was always this nagging issue. WordPress. is a hosted service and cost money if you want more then the vanilla. And I had no interest in hosting WordPress elseware. It also was missing a plug-in that allowed you to to reference GitHub files directly. Copying code from a GitHub project into Gist seems like a major waste of time. Enter Jekyll A few months ago I listened to a podcast where they briefly mentioned github pages. This perked my interest. Partially because if was free, and partly because if was GitHub. (I also like the idea of bloging like a hacker). I figured if it was hosted on GitHub, surely they would have a easy way to reference a GitHub Repo. No such luck. The default plugins were somewhat limited. If I wanted unsupported plugins I had to compile the site locally. So I looked at all the plugins for Jekyll in Ruby Gems, I came across one that worked. However it only wanted to work in Jekyll version 1. I looked at the code on it’s GitHub repo and it looked to be an arbitrary requirement. So I set out the setup Jekyll using Bash on Windows 10. I plan to write a full blog on the experience. There were many bumps and gatcha’s getting here, but I now can reference sections of GitHub Repos files in my posts, so it was worth it.Windows Image Tools \| Modify VHDX2016-08-30T16:48:59-05:002016-08-30T16:48:59-05:00https://www.bladefirelight.com/windowsimagetools/Modifying-Fresh-VHDX<h2 id="continuing-onward">Continuing onward</h2>
<p>In the last post I talked about Unattend.xml and using WindowsImageTools to create a new VHDX with an Unattend.xml and FirstBoot.ps1 for it to run.</p>
<p>using that as a starting point I’m going to replace the do-nothing FirstBoot.ps1 add have it so something more interesting.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>You will need the VHDX created at the end of my post on New-UnattendXml and obviously a copy on WindowsImageTools from the PowerShell gallery.</p>
<p>We are going to have our VHDX install <a href="http://www.7-zip.org/download.html">7Zip</a> on first boot so you will need to download a the MSI if your following along.</p>
<h2 id="creating-a-new-improved-firstbootps1">Creating a new improved FirstBoot.ps1</h2>
<p>I downloaded my MSI into G:\filesToInject\PsTemp and now i’m going to replace firstboot.ps1 so it will silently install it and write a log and shutdown</p>
<pre><code class="language-PowerShell">$BetterFirstBootContent = {
start-process C:\PsTemp\7z1602-x64.msi -ArgumentList '/q','INSTALLDIR="C:\Program Files\7-Zip"' -wait
}
New-Item -Path "G:\filesToInject\PsTemp" -Name FirstBoot.ps1 -ItemType 'file' -Value $BetterFirstBootContent -Force
</code></pre>
<h3 id="placing-the-files-in-the-vhdx">Placing the files in the VHDX</h3>
<p>So now we need to get the two fils into our existing VHD. Now I could just run Convert-Wim2VHD again with the same commands and I would get the desired effect, but I dont want to wait for the that process. I also could just mount the VHDX and drag and drop the files into the VHDX, but that would not let me show off another function in WindowsImageTools</p>
<h4 id="mount-vhdandrunblock">Mount-VhdAndRunBlock</h4>
<p>Mount-VhdAndRunBlock is one of my favorite tools. It will mount a VHD and run a script block allowing me to manipulate the files inside. This can be used for copying in files, Editing the registry, checking if a file exists and so on.</p>
<pre><code class="language-PowerShell">$ScriptBlock = {
copy G:\filesToInject\PsTemp\*.* "$($driveletter):\PsTemp\" -Force
}
Mount-VhdAndRunBlock -VHD 'G:\vhd\2012r2_eval_Core.vhdx' -block $ScriptBlock
</code></pre>
<p>Now that we have the better first boot script we need to create a VM to attach the vhdx to. for that I’m going to use Invoke-CreateVmRunAndWait.</p>
<h3 id="before-we-create-a-vm">Before we create a VM</h3>
<p>Lets look at what we need besides the vhdx to use Invoke-CreateVmRunAndWait</p>
<pre><code class="language-PowerShell">help Invoke-CreateVmRunAndWait
NAME
Invoke-CreateVmRunAndWait
SYNOPSIS
Create a temp vm with a random name and wait for it to stop
SYNTAX
Invoke-CreateVmRunAndWait [-VhdPath] <String> [-VmGeneration] <Int32> [-VmSwitch] <String> [[-vLan] <Int32>]
[[-ProcessorCount] <Int32>] [[-MemoryStartupBytess] <Int64>] [<CommonParameters>]
</code></pre>
<p>So according to the syntax VhdPath, VmGeneration and VmSwitch are required parameters.</p>
<h4 id="deciding-on-a-vm-generation">Deciding on a VM Generation</h4>
<p>While I know what type of VHDX i created, when creating automation script we may not always know if I need a Gen1 or Gen2 VM. So I’m going use Get-VhdPartitionStyle.</p>
<p>Get-VhdPartitionStyle will return a string of either MBR for Master Boot Record or GPT for GUID Partition Table. Generation 1 VM’s are based on BIOS and windows only works with MBR partitions for BIOS based computers. Generation 2 VM’s are based on uEFI architecture and Windows uses GPT partitions by default with uEFI.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$vmGeneration</span> <span class="o">=</span> 2 <span class="c1">#default to gen2 </span>
<span class="nv">$PartitionStyle</span> <span class="o">=</span> Get-VhdPartitionStyle -vhd <span class="s1">'G:\vhd\2012r2_eval_Core.vhdx'</span>
<span class="k">if</span> <span class="o">(</span><span class="nv">$PartitionStyle</span> -eq <span class="s1">'GPT'</span><span class="o">)</span>
<span class="o">{</span>
<span class="nv">$vmGeneration</span> <span class="o">=</span> 2
<span class="o">}</span>
</code></pre></div></div>
<h4 id="getting-the-switch">Getting the switch</h4>
<p>We are not actually going to use the network in this example so I’m just going to grab the first VmSwitch</p>
<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$VmSwitch</span> <span class="o">=</span> <span class="o">(</span>Get-VMSwitch<span class="o">)[</span>0].Name <span class="c1">#First switch</span></code></pre></figure>
<h3 id="creating-the-vm">Creating the VM</h3>
<pre><code class="language-PowerShell">Invoke-CreateVmRunAndWait -VhdPath 'G:\vhd\2012r2_eval_Core.vhdx' -VmGeneration $vmGeneration -VmSwitch $VmSwitch -verbose
</code></pre>
<p>We now have a randomly named vm that will run our script and install z7ip. Because it running this during the specialize phase once it’s done it will reboot and end up at the “Press Ctrl+Alt+Del to login” I”m just going to use Hyper-V manager to shut it down.</p>
<pre><code class="language-PowerShell">VERBOSE: [Invoke-CreateVmRunAndWait] : Creating VM 1utbdwad at 08/30/2016 10:57:29
VERBOSE: [Invoke-CreateVmRunAndWait] : VM 1utbdwad stopped
VERBOSE: [Invoke-CreateVmRunAndWait] : VM 1utbdwad Deleted at 08/30/2016 11:08:47
</code></pre>
<h3 id="checking-the-results">Checking the results</h3>
<pre><code class="language-PowerShell">$ScriptBlock = {
dir "$($driveletter):\Program Files\"
}
Mount-VhdAndRunBlock -VHD 'G:\vhd\2012r2_eval_Core.vhdx' -block $ScriptBlock
</code></pre>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Directory: K:\Program Files
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 9/4/2016 9:42 AM 7-Zip
d----- 8/22/2013 10:39 AM Common Files
d----- 3/21/2014 1:09 PM Internet Explorer
d----- 8/22/2013 10:39 AM WindowsPowerShell
</code></pre></div></div>
<h2 id="summary">Summary</h2>
<p>So now we have a VM with an app installed, users created and automated the Out of Box Esperance. However a fresh install of Server 2012 R2 does not have WMF 5 and will have a ton of patches that need to be installed.</p>
<p>I will tackle that next.</p>David JonesBladeFireLight@outlook.comOne of the time consuming steps to deploying new VMs is the time spend managing Images and and applying patches. I’m not big on Golden images. I tend to use a fully patched VHDX or VMDK and let DSC handle the configuration and software. This is not the fastest, and at scale you need to create more then one image based on what saves the most time. (IIS, SQL, Exchange, etc…).Windows Image Tools \| Unattend.xml2016-08-22T16:48:59-05:002016-08-22T16:48:59-05:00https://www.bladefirelight.com/windowsimagetools/Unattend<h2 id="getting-started">Getting started</h2>
<p>The second item need to automate the creation of a VM is a way to bootstrap said automation of a fresh windows image. That requires an unattend.xml</p>
<h2 id="prerequisites">prerequisites</h2>
<p>Now to work with Unattent.xml you need a deep understanding of XML structure and namespace… Just kidding. I’m going to use New-UnattendXml part of WindowsImageTools</p>
<p>You can install WindowsImageTools from the PowerShell Gallery with install-module WindowsImageTools</p>
<h2 id="creating-a-basic-unattentxml">Creating a basic Unattent.xml</h2>
<p>Lets start off with a basic Unattend. Minimum requirements for this is changing the administrator password</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>New-UnattendXml -AdminCredential <span class="o">(</span><span class="nb">Get-Credential</span><span class="o">)</span> |
<span class="nb">Get-Content</span>
</code></pre></div></div>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cmdlet <span class="nb">Get-Credential </span>at <span class="nb">command </span>pipeline position 1
Supply values <span class="k">for </span>the following parameters:
Credential
WARNING: C:\Users\BLADE_~1\AppData\Local\Temp\hqfyjyg2\unattend.xml only usable on a server SKU, <span class="k">for </span>a client OS, use
either -EnableAdministrator or -UserAccount
</code></pre></div></div>
<p>Notice the warning. Setting the Admin password on a server is all you need, as the build in Administrator account is allready enabled. On a client OS you have to either enable the administrator or add a second one or three.
It spits out this mass of XML</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?xml version="1.0" encoding="utf-8"?></span>
<span class="nt"><unattend</span> <span class="na">xmlns=</span><span class="s">"urn:schemas-microsoft-com:unattend"</span><span class="nt">></span>
<span class="nt"><settings</span> <span class="na">pass=</span><span class="s">"specialize"</span><span class="nt">></span>
<span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Deployment"</span> <span class="na">processorArchitecture=</span><span class="s">"amd64"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"></component></span>
<span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Deployment"</span> <span class="na">processorArchitecture=</span><span class="s">"x86"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"></component></span>
<span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Shell-Setup"</span> <span class="na">processorArchitecture=</span><span class="s">"amd64"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"><ComputerName></span>*<span class="nt"></ComputerName></span>
<span class="nt"></component></span>
<span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Shell-Setup"</span> <span class="na">processorArchitecture=</span><span class="s">"x86"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"><ComputerName></span>*<span class="nt"></ComputerName></span>
<span class="nt"></component></span>
<span class="nt"></settings></span>
<span class="nt"><settings</span> <span class="na">pass=</span><span class="s">"oobeSystem"</span><span class="nt">></span>
<span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-International-Core"</span> <span class="na">processorArchitecture=</span><span class="s">"amd64"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"><InputLocale></span>en-US<span class="nt"></InputLocale></span>
<span class="nt"><SystemLocale></span>en-US<span class="nt"></SystemLocale></span>
<span class="nt"><UILanguage></span>en-US<span class="nt"></UILanguage></span>
<span class="nt"><UserLocale></span>en-US<span class="nt"></UserLocale></span>
<span class="nt"></component></span>
<span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-International-Core"</span> <span class="na">processorArchitecture=</span><span class="s">"x86"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"><InputLocale></span>en-US<span class="nt"></InputLocale></span>
<span class="nt"><SystemLocale></span>en-US<span class="nt"></SystemLocale></span>
<span class="nt"><UILanguage></span>en-US<span class="nt"></UILanguage></span>
<span class="nt"><UserLocale></span>en-US<span class="nt"></UserLocale></span>
<span class="nt"></component></span>
<span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Shell-Setup"</span> <span class="na">processorArchitecture=</span><span class="s">"amd64"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"><OOBE></span>
<span class="nt"><HideEULAPage></span>true<span class="nt"></HideEULAPage></span>
<span class="nt"><HideWirelessSetupInOOBE></span>true<span class="nt"></HideWirelessSetupInOOBE></span>
<span class="nt"><NetworkLocation></span>Work<span class="nt"></NetworkLocation></span>
<span class="nt"><ProtectYourPC></span>1<span class="nt"></ProtectYourPC></span>
<span class="nt"><SkipUserOOBE></span>true<span class="nt"></SkipUserOOBE></span>
<span class="nt"><SkipMachineOOBE></span>true<span class="nt"></SkipMachineOOBE></span>
<span class="nt"></OOBE></span>
<span class="nt"><TimeZone></span>GMT Standard Time<span class="nt"></TimeZone></span>
<span class="nt"><UserAccounts></span>
<span class="nt"><AdministratorPassword></span>
<span class="nt"><Value></span>YQBkAHMAZgBBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByAFAAYQBzAHMAdwBvAHIAZAA=<span class="nt"></Value></span>
<span class="nt"><PlainText></span>false<span class="nt"></PlainText></span>
<span class="nt"></AdministratorPassword></span>
<span class="nt"></UserAccounts></span>
<span class="nt"><RegisteredOrganization></span>Generic Organization<span class="nt"></RegisteredOrganization></span>
<span class="nt"><RegisteredOwner></span>Generic Owner<span class="nt"></RegisteredOwner></span>
<span class="nt"></component></span>
<span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Shell-Setup"</span> <span class="na">processorArchitecture=</span><span class="s">"x86"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"><OOBE></span>
<span class="nt"><HideEULAPage></span>true<span class="nt"></HideEULAPage></span>
<span class="nt"><HideWirelessSetupInOOBE></span>true<span class="nt"></HideWirelessSetupInOOBE></span>
<span class="nt"><NetworkLocation></span>Work<span class="nt"></NetworkLocation></span>
<span class="nt"><ProtectYourPC></span>1<span class="nt"></ProtectYourPC></span>
<span class="nt"><SkipUserOOBE></span>true<span class="nt"></SkipUserOOBE></span>
<span class="nt"><SkipMachineOOBE></span>true<span class="nt"></SkipMachineOOBE></span>
<span class="nt"></OOBE></span>
<span class="nt"><TimeZone></span>GMT Standard Time<span class="nt"></TimeZone></span>
<span class="nt"><UserAccounts></span>
<span class="nt"><AdministratorPassword></span>
<span class="nt"><Value></span>YQBkAHMAZgBBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByAFAAYQBzAHMAdwBvAHIAZAA=<span class="nt"></Value></span>
<span class="nt"><PlainText></span>false<span class="nt"></PlainText></span>
<span class="nt"></AdministratorPassword></span>
<span class="nt"></UserAccounts></span>
<span class="nt"><RegisteredOrganization></span>Generic Organization<span class="nt"></RegisteredOrganization></span>
<span class="nt"><RegisteredOwner></span>Generic Owner<span class="nt"></RegisteredOwner></span>
<span class="nt"></component></span>
<span class="nt"></settings></span>
<span class="nt"></unattend></span>
</code></pre></div></div>
<h2 id="anatomy-of-unattentxml">Anatomy of Unattent.xml</h2>
<p>Takeing a closer look at each section</p>
<h3 id="specialize--deployment">Specialize : Deployment</h3>
<p>Contrary to popular belief, the unattend.xml can have one file that can be use for both 32 and 64bit. All you have to do is repeat the section with a different architecture. With one caveat that I will get into later. In the basic Unattent the Deployment section is empty.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Deployment"</span> <span class="na">processorArchitecture=</span><span class="s">"amd64"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"></component></span>
<span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Deployment"</span> <span class="na">processorArchitecture=</span><span class="s">"x86"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"></component></span>
</code></pre></div></div>
<h3 id="specialize--shell-setup">Specialize : Shell Setup</h3>
<p>Basic settings lets windows pick a random computer name.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Shell-Setup"</span> <span class="na">processorArchitecture=</span><span class="s">"amd64"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"><ComputerName></span>*<span class="nt"></ComputerName></span>
<span class="nt"></component></span>
<span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Shell-Setup"</span> <span class="na">processorArchitecture=</span><span class="s">"x86"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"><ComputerName></span>*<span class="nt"></ComputerName></span>
<span class="nt"></component></span>
</code></pre></div></div>
<h3 id="oobesystem--international-core">oobeSystem : International Core</h3>
<p>The language and input are localized to en-US by default, This can be changed via parameters</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-International-Core"</span> <span class="na">processorArchitecture=</span><span class="s">"amd64"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"><InputLocale></span>en-US<span class="nt"></InputLocale></span>
<span class="nt"><SystemLocale></span>en-US<span class="nt"></SystemLocale></span>
<span class="nt"><UILanguage></span>en-US<span class="nt"></UILanguage></span>
<span class="nt"><UserLocale></span>en-US<span class="nt"></UserLocale></span>
<span class="nt"></component></span>
<span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-International-Core"</span> <span class="na">processorArchitecture=</span><span class="s">"x86"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"><InputLocale></span>en-US<span class="nt"></InputLocale></span>
<span class="nt"><SystemLocale></span>en-US<span class="nt"></SystemLocale></span>
<span class="nt"><UILanguage></span>en-US<span class="nt"></UILanguage></span>
<span class="nt"><UserLocale></span>en-US<span class="nt"></UserLocale></span>
<span class="nt"></component></span>
</code></pre></div></div>
<h3 id="oobesystem---shell-setup">oobeSystem : Shell-Setup</h3>
<p>This section is a bit larger. It going to hide or skip everything it can to avoid any prompting. Afterall this is suposed to be automated.
We also set the Timezone to a default of GMT and set the admin password
Now the Admin Password may look encrypted, but it’s not that is 64bit encodeing. The best thing to do is set a password here for troubleshooting, then change it via LAPS or DSC once windows is up and running.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Shell-Setup"</span> <span class="na">processorArchitecture=</span><span class="s">"amd64"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"><OOBE></span>
<span class="nt"><HideEULAPage></span>true<span class="nt"></HideEULAPage></span>
<span class="nt"><HideWirelessSetupInOOBE></span>true<span class="nt"></HideWirelessSetupInOOBE></span>
<span class="nt"><NetworkLocation></span>Work<span class="nt"></NetworkLocation></span>
<span class="nt"><ProtectYourPC></span>1<span class="nt"></ProtectYourPC></span>
<span class="nt"><SkipUserOOBE></span>true<span class="nt"></SkipUserOOBE></span>
<span class="nt"><SkipMachineOOBE></span>true<span class="nt"></SkipMachineOOBE></span>
<span class="nt"></OOBE></span>
<span class="nt"><TimeZone></span>GMT Standard Time<span class="nt"></TimeZone></span>
<span class="nt"><UserAccounts></span>
<span class="nt"><AdministratorPassword></span>
<span class="nt"><Value></span>YQBkAHMAZgBBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByAFAAYQBzAHMAdwBvAHIAZAA=<span class="nt"></Value></span>
<span class="nt"><PlainText></span>false<span class="nt"></PlainText></span>
<span class="nt"></AdministratorPassword></span>
<span class="nt"></UserAccounts></span>
<span class="nt"><RegisteredOrganization></span>Generic Organization<span class="nt"></RegisteredOrganization></span>
<span class="nt"><RegisteredOwner></span>Generic Owner<span class="nt"></RegisteredOwner></span>
<span class="nt"></component></span>
<span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Shell-Setup"</span> <span class="na">processorArchitecture=</span><span class="s">"x86"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"><OOBE></span>
<span class="nt"><HideEULAPage></span>true<span class="nt"></HideEULAPage></span>
<span class="nt"><HideWirelessSetupInOOBE></span>true<span class="nt"></HideWirelessSetupInOOBE></span>
<span class="nt"><NetworkLocation></span>Work<span class="nt"></NetworkLocation></span>
<span class="nt"><ProtectYourPC></span>1<span class="nt"></ProtectYourPC></span>
<span class="nt"><SkipUserOOBE></span>true<span class="nt"></SkipUserOOBE></span>
<span class="nt"><SkipMachineOOBE></span>true<span class="nt"></SkipMachineOOBE></span>
<span class="nt"></OOBE></span>
<span class="nt"><TimeZone></span>GMT Standard Time<span class="nt"></TimeZone></span>
<span class="nt"><UserAccounts></span>
<span class="nt"><AdministratorPassword></span>
<span class="nt"><Value></span>YQBkAHMAZgBBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByAFAAYQBzAHMAdwBvAHIAZAA=<span class="nt"></Value></span>
<span class="nt"><PlainText></span>false<span class="nt"></PlainText></span>
<span class="nt"></AdministratorPassword></span>
<span class="nt"></UserAccounts></span>
<span class="nt"><RegisteredOrganization></span>Generic Organization<span class="nt"></RegisteredOrganization></span>
<span class="nt"><RegisteredOwner></span>Generic Owner<span class="nt"></RegisteredOwner></span>
<span class="nt"></component></span>
</code></pre></div></div>
<p>This is all well and good but it’s still only get’s us the first step. To automate deployment we need to do more then bypass prompts and set a password.</p>
<h2 id="more-usefull-unattentxml">More usefull Unattent.xml</h2>
<p>Now we will set a few more items.</p>
<pre><code class="language-PowerShell">$param = @{
AdminCredential = (Get-Credential)
UserAccount = (Get-Credential), (Get-Credential)
FirstBootScriptPath = 'C:\PsTemp\FirstBoot.ps1'
TimeZone = 'Central Standard Time'
RegisteredOwner = 'Employee'
RegisteredOrganization = 'Contoso'
enableAdministrator = $true
}
New-UnattendXml @param |
get-content
</code></pre>
<h3 id="specialize---deployment">specialize : Deployment</h3>
<p>One thing to notice here is the amd64 component is still blank. The reason for this is 64bit OS’s will run the commands in both the 32 and 64bit sections.</p>
<p>We have two commands in the RunSynchronous section. The first one enables the Administrator account. the second launches PowerShell with the script path we asked for.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Deployment"</span> <span class="na">processorArchitecture=</span><span class="s">"amd64"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"></component></span>
<span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Deployment"</span> <span class="na">processorArchitecture=</span><span class="s">"x86"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"><RunSynchronous></span>
<span class="nt"><RunSynchronousCommand</span> <span class="na">wcm:action=</span><span class="s">"add"</span><span class="nt">></span>
<span class="nt"><Description></span>Enable Administrator<span class="nt"></Description></span>
<span class="nt"><Order></span>1<span class="nt"></Order></span>
<span class="nt"><Path></span>net user administrator /active:yes<span class="nt"></Path></span>
<span class="nt"></RunSynchronousCommand></span>
<span class="nt"><RunSynchronousCommand</span> <span class="na">wcm:action=</span><span class="s">"add"</span><span class="nt">></span>
<span class="nt"><Description></span>PowerShell First boot script<span class="nt"></Description></span>
<span class="nt"><Order></span>2<span class="nt"></Order></span>
<span class="nt"><Path></span>%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\PsTemp\FirstBoot.ps1"<span class="nt"></Path></span>
<span class="nt"></RunSynchronousCommand></span>
<span class="nt"></RunSynchronous></span>
<span class="nt"></component></span>
</code></pre></div></div>
<h3 id="oobesystem---shell-setup-1">oobeSystem : Shell Setup</h3>
<p>The next section that is diferent is the shell setup. You will notice I set two additional user accounts. Remember that the passwords are encoded not encrypted. I also have entries to set the ownership.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Shell-Setup"</span> <span class="na">processorArchitecture=</span><span class="s">"amd64"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"><OOBE></span>
<span class="nt"><HideEULAPage></span>true<span class="nt"></HideEULAPage></span>
<span class="nt"><HideWirelessSetupInOOBE></span>true<span class="nt"></HideWirelessSetupInOOBE></span>
<span class="nt"><NetworkLocation></span>Work<span class="nt"></NetworkLocation></span>
<span class="nt"><ProtectYourPC></span>1<span class="nt"></ProtectYourPC></span>
<span class="nt"><SkipUserOOBE></span>true<span class="nt"></SkipUserOOBE></span>
<span class="nt"><SkipMachineOOBE></span>true<span class="nt"></SkipMachineOOBE></span>
<span class="nt"></OOBE></span>
<span class="nt"><TimeZone></span>Central Standard Time<span class="nt"></TimeZone></span>
<span class="nt"><UserAccounts></span>
<span class="nt"><AdministratorPassword></span>
<span class="nt"><Value></span>UABAAHMAcwB3ADAAcgBkAEEAZABtAGkAbgBpAHMAdAByAGEAdABvAHIAUABhAHMAcwB3AG8AcgBkAA==<span class="nt"></Value></span>
<span class="nt"><PlainText></span>false<span class="nt"></PlainText></span>
<span class="nt"></AdministratorPassword></span>
<span class="nt"><LocalAccounts></span>
<span class="nt"><LocalAccount</span> <span class="na">wcm:action=</span><span class="s">"add"</span><span class="nt">></span>
<span class="nt"><Password></span>
<span class="nt"><Value></span>UABAAHMAcwB3ADAAcgBkADEAMgAzAFAAYQBzAHMAdwBvAHIAZAA=<span class="nt"></Value></span>
<span class="nt"><PlainText></span>false<span class="nt"></PlainText></span>
<span class="nt"></Password></span>
<span class="nt"><DisplayName></span>Don<span class="nt"></DisplayName></span>
<span class="nt"><Group></span>Administrators<span class="nt"></Group></span>
<span class="nt"><Name></span>Don<span class="nt"></Name></span>
<span class="nt"></LocalAccount></span>
<span class="nt"><LocalAccount</span> <span class="na">wcm:action=</span><span class="s">"add"</span><span class="nt">></span>
<span class="nt"><Password></span>
<span class="nt"><Value></span>UABAAHMAcwB3ADAAcgBkADQANQA2AFAAYQBzAHMAdwBvAHIAZAA=<span class="nt"></Value></span>
<span class="nt"><PlainText></span>false<span class="nt"></PlainText></span>
<span class="nt"></Password></span>
<span class="nt"><DisplayName></span>Mike<span class="nt"></DisplayName></span>
<span class="nt"><Group></span>Administrators<span class="nt"></Group></span>
<span class="nt"><Name></span>Mike<span class="nt"></Name></span>
<span class="nt"></LocalAccount></span>
<span class="nt"></LocalAccounts></span>
<span class="nt"></UserAccounts></span>
<span class="nt"><RegisteredOrganization></span>Contoso<span class="nt"></RegisteredOrganization></span>
<span class="nt"><RegisteredOwner></span>Employee<span class="nt"></RegisteredOwner></span>
<span class="nt"></component></span>
<span class="nt"><component</span> <span class="na">name=</span><span class="s">"Microsoft-Windows-Shell-Setup"</span> <span class="na">processorArchitecture=</span><span class="s">"x86"</span> <span class="na">publicKeyToken=</span><span class="s">"31bf3856ad364e35"</span> <span class="na">language=</span><span class="s">"neutral"</span> <span class="na">versionScope=</span><span class="s">"nonSxS"</span> <span class="na">xmlns:wcm=</span><span class="s">"http://schemas.microsoft.com/WMIConfig/2002/State"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span><span class="nt">></span>
<span class="nt"><OOBE></span>
<span class="nt"><HideEULAPage></span>true<span class="nt"></HideEULAPage></span>
<span class="nt"><HideWirelessSetupInOOBE></span>true<span class="nt"></HideWirelessSetupInOOBE></span>
<span class="nt"><NetworkLocation></span>Work<span class="nt"></NetworkLocation></span>
<span class="nt"><ProtectYourPC></span>1<span class="nt"></ProtectYourPC></span>
<span class="nt"><SkipUserOOBE></span>true<span class="nt"></SkipUserOOBE></span>
<span class="nt"><SkipMachineOOBE></span>true<span class="nt"></SkipMachineOOBE></span>
<span class="nt"></OOBE></span>
<span class="nt"><TimeZone></span>Central Standard Time<span class="nt"></TimeZone></span>
<span class="nt"><UserAccounts></span>
<span class="nt"><AdministratorPassword></span>
<span class="nt"><Value></span>UABAAHMAcwB3ADAAcgBkAEEAZABtAGkAbgBpAHMAdAByAGEAdABvAHIAUABhAHMAcwB3AG8AcgBkAA==<span class="nt"></Value></span>
<span class="nt"><PlainText></span>false<span class="nt"></PlainText></span>
<span class="nt"></AdministratorPassword></span>
<span class="nt"><LocalAccounts></span>
<span class="nt"><LocalAccount</span> <span class="na">wcm:action=</span><span class="s">"add"</span><span class="nt">></span>
<span class="nt"><Password></span>
<span class="nt"><Value></span>UABAAHMAcwB3ADAAcgBkADEAMgAzAFAAYQBzAHMAdwBvAHIAZAA=<span class="nt"></Value></span>
<span class="nt"><PlainText></span>false<span class="nt"></PlainText></span>
<span class="nt"></Password></span>
<span class="nt"><DisplayName></span>Don<span class="nt"></DisplayName></span>
<span class="nt"><Group></span>Administrators<span class="nt"></Group></span>
<span class="nt"><Name></span>Don<span class="nt"></Name></span>
<span class="nt"></LocalAccount></span>
<span class="nt"><LocalAccount</span> <span class="na">wcm:action=</span><span class="s">"add"</span><span class="nt">></span>
<span class="nt"><Password></span>
<span class="nt"><Value></span>UABAAHMAcwB3ADAAcgBkADQANQA2AFAAYQBzAHMAdwBvAHIAZAA=<span class="nt"></Value></span>
<span class="nt"><PlainText></span>false<span class="nt"></PlainText></span>
<span class="nt"></Password></span>
<span class="nt"><DisplayName></span>Mike<span class="nt"></DisplayName></span>
<span class="nt"><Group></span>Administrators<span class="nt"></Group></span>
<span class="nt"><Name></span>Mike<span class="nt"></Name></span>
<span class="nt"></LocalAccount></span>
<span class="nt"></LocalAccounts></span>
<span class="nt"></UserAccounts></span>
<span class="nt"><RegisteredOrganization></span>Contoso<span class="nt"></RegisteredOrganization></span>
<span class="nt"><RegisteredOwner></span>Employee<span class="nt"></RegisteredOwner></span>
<span class="nt"><FirstLogonCommands</span> <span class="nt">/></span>
<span class="nt"><LogonCommands</span> <span class="nt">/></span>
<span class="nt"></component></span>
</code></pre></div></div>
<h2 id="puting-it-together">Puting it together</h2>
<p>New-UnattentXML returns a path object to the file created, and by default that is in $env:TEMP. so we will store the path in a variable and use it pluss the path to our FirstBoot script when creating the VHDX</p>
<h3 id="adding-the-unattend-and-script-file-when-creating-a-vhdx">Adding the unattend and script file when creating a VHDX</h3>
<pre><code class="language-PowerShell">$UnattentParam = @{
AdminCredential = (Get-Credential)
UserAccount = (Get-Credential), (Get-Credential)
FirstBootScriptPath = 'C:\PsTemp\FirstBoot.ps1'
TimeZone = 'Central Standard Time'
RegisteredOwner = 'Employee'
RegisteredOrganization = 'Contoso'
enableAdministrator = $true
}
$UnattentPath = (New-UnattendXml @UnattentParam ).FullName
$FirstBootContent = {
## Do something Cool
}
New-Item -Path "G:\filesToInject\PsTemp" -Name FirstBoot.ps1 -ItemType 'file' -Value $FirstBootContent
$ConverParm = @{
Path = 'G:\vhd\2012r2_eval_Core.vhdx'
Size = 60gb
Dynamic = $true
DiskLayout = 'UEFI'
SourcePath = 'G:\iso\Srv2012r2Eval.ISO'
Index = 1
Feature = 'NetFx3'
Unattend = $UnattentPath
filesToInject = 'G:\filesToInject\PsTemp'
}
Convert-Wim2VHD @ConverParm -Verbose
</code></pre>
<p>Below is part of the verbose output that covers Unattend and filesToInject</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>VERBOSE: [Set-VHDPartition] [2012r2_eval_Core.vhdx] Windows Partition [3] : Adding files from G:\filesToInject
VERBOSE: [Set-VHDPartition] [2012r2_eval_Core.vhdx] Windows Partition [3] : Adding Unattend.xml (C:\Users\Blade_000\AppData\Local\Temp\djebkgrz\unattend.xml)
</code></pre></div></div>
<p>So now when this vhdx first boots it will process Unattend creating users and running our PowerShell script</p>David JonesBladeFireLight@outlook.comOne of the time consuming steps to deploying new VMs is the time spend managing Images and and applying patches. I’m not big on Golden images. I tend to use a fully patched VHDX or VMDK and let DSC handle the configuration and software. This is not the fastest, and at scale you need to create more then one image based on what saves the most time. (IIS, SQL, Exchange, etc…).Windows Image Tools \| VHDX Creation2016-08-21T16:48:59-05:002016-08-21T16:48:59-05:00https://www.bladefirelight.com/windowsimagetools/VHDXCreation<p>## Getting started</p>
<p>To automate the creation of clean VM’s we need a clean diskimage. for that we will use Convert-Wim2VHD from WindowsImageTools</p>
<p>Before you can create a VHDX your going to need a source. either a WIM or ISO. In tha lab that would usually be a trial version. For work, the iso from your licencing portal, or heaven forbid a physical CD.</p>
<p>You can get an evaluation copy of windows from <a href="https://www.microsoft.com/en-us/evalcenter">TechNet Evaluation Center</a></p>
<p>I’m going to use a evaluation of 2012r2. I have it downloaded and saved to G:\ISO\Srv2012r2Eval.iso and i’m going to create a new vhdx in G:\vhd</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Convert-Wim2VHD -Path G:\vhd\2012r2_eval_Core.vhdx -Size 60gb -Dynamic -DiskLayout UEFI -SourcePath G:\iso\Srv2012r2Eval.ISO -Index 1 -Verbose
</code></pre></div></div>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>VERBOSE: <span class="o">[</span>Convert-Wim2VHD] : Overwrite partitions inside <span class="o">[</span>G:\vhd\2012r2_eval_Core.vhdx]
with content of <span class="o">[</span>G:\iso\Srv2012r2Eval.ISO]
VERBOSE: <span class="o">[</span>Convert-Wim2VHD] : InitializeVHDPartitionParam
VERBOSE:
Name Value
---- -----
Dynamic True
Path G:\vhd\2012r2_eval_Core.vhdx
DiskLayout UEFI
force True
Size 64424509440
VERBOSE: <span class="o">[</span>Convert-Wim2VHD] : SetVHDPartitionParam
VERBOSE:
Name Value
---- -----
SourcePath G:\iso\Srv2012r2Eval.ISO
Path G:\vhd\2012r2_eval_Core.vhdx
FeatureSourceIndex 1
Index 1
Force True
Confirm False
VERBOSE: <span class="o">[</span>Convert-Wim2VHD] : ParametersToPass
VERBOSE:
Name Value
---- -----
Verbose True
VERBOSE: <span class="o">[</span>Initialize-VHDPartition] Create partition structure <span class="k">for </span>Bootable vhd<span class="o">(</span>x<span class="o">)</span> on
<span class="o">[</span>G:\vhd\2012r2_eval_Core.vhdx]
VERBOSE: <span class="o">[</span>Initialize-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : Creating
VERBOSE: <span class="o">[</span>Initialize-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : @vhdParms
VERBOSE:
Name Value
---- -----
Path G:\vhd\2012r2_eval_Core.vhdx
SizeBytes 64424509440
Dynamic True
ErrorAction Stop
BlockSizeBytes 1048576
VERBOSE: <span class="o">[</span>Initialize-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : Mounting disk image
VERBOSE: <span class="o">[</span>Initialize-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : Initializing disk <span class="o">[</span>5] as
GPT
VERBOSE: <span class="o">[</span>Initialize-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : Clearing disk partitions to
<span class="nb">start </span>all over
VERBOSE: <span class="o">[</span>Initialize-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : EFI system : Creating
partition of <span class="o">[</span>209715200] bytes
VERBOSE: <span class="o">[</span>Initialize-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : EFI system : Formatting
FAT32
VERBOSE: <span class="o">[</span>Initialize-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : EFI system : Setting system
partition as ESP
VERBOSE: <span class="o">[</span>Initialize-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : MSR : Creating partition of
<span class="o">[</span>134217728] bytes
VERBOSE: <span class="o">[</span>Initialize-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : Windows : Creating partition
of <span class="o">[</span>64078479360] bytes
VERBOSE: <span class="o">[</span>Initialize-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : Windows : Formatting volume
NTFS
VERBOSE: <span class="o">[</span>Initialize-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : Dismounting disk image
VERBOSE: <span class="o">[</span><span class="nb">Set</span>-VHDPartition] : Overwrite partitions inside <span class="o">[</span>G:\vhd\2012r2_eval_Core.vhdx]
with content of
<span class="o">[</span>G:\iso\Srv2012r2Eval.ISO]
VERBOSE: <span class="o">[</span><span class="nb">Set</span>-VHDPartition] : Opening ISO <span class="o">[</span>Srv2012r2Eval.ISO]
VERBOSE: <span class="o">[</span><span class="nb">Set</span>-VHDPartition] : Looking <span class="k">for </span>I:\sources\install.wim
VERBOSE: <span class="o">[</span><span class="nb">Set</span>-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : Mounting disk image
<span class="o">[</span>G:\vhd\2012r2_eval_Core.vhdx]
VERBOSE: <span class="o">[</span><span class="nb">Set</span>-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : Munted as disknumber <span class="o">[</span>5]
VERBOSE: <span class="o">[</span><span class="nb">Set</span>-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : Partition Table
VERBOSE:
PartitionNumber DriveLetter Size <span class="nb">Type</span>
--------------- ----------- ---- ----
1 J 209715200 System
2 134217728 Reserved
3 K 64078479360 Basic
VERBOSE: <span class="o">[</span><span class="nb">Set</span>-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] Windows Partition <span class="o">[</span>3] : Applying
image <span class="k">from</span>
<span class="o">[</span>I:\sources\install.wim] to <span class="o">[</span>K:\] <span class="k">using </span>Index <span class="o">[</span>1]
VERBOSE: <span class="o">[</span><span class="nb">Set</span>-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : Disk Layout <span class="o">[</span>UEFI]
VERBOSE: <span class="o">[</span><span class="nb">Set</span>-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] System Partition <span class="o">[</span>1] : Running
<span class="o">[</span>K:\Windows\System32\bcdboot.exe] ->
K:\Windows /s J: /v /f UEFI
VERBOSE: <span class="o">[</span>Run-Executable] : Running <span class="o">[</span>K:\Windows\System32\bcdboot.exe]
<span class="o">[</span>K:\Windows /s J: /v /f UEFI]
VERBOSE:
Name Value
---- -----
PassThru True
FilePath K:\Windows\System32\bcdboot.exe
ArgumentList <span class="o">{</span>K:\Windows, /s J:, /v, /f UEFI<span class="o">}</span>
RedirectStandardError C:\Users\BLADE_~1\AppData\Local\Temp\bcdboot.exe-Standard
Error.txt
NoNewWindow True
Wait True
RedirectStandardOutput C:\Users\BLADE_~1\AppData\Local\Temp\bcdboot.exe-Standard
Output.txt
VERBOSE: <span class="o">[</span>Run-Executable] : <span class="k">Return </span>code was <span class="o">[</span>0]
VERBOSE: <span class="o">[</span><span class="nb">Set</span>-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : Removing Drive letters
VERBOSE: <span class="o">[</span><span class="nb">Set</span>-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : Dismounting
VERBOSE: <span class="o">[</span><span class="nb">Set</span>-VHDPartition] <span class="o">[</span>2012r2_eval_Core.vhdx] : Finished
</code></pre></div></div>
<h2 id="server-with-a-gui-and-net-35">Server with a GUI and .net 3.5</h2>
<p>Lets say we need a VM with the Desktop and .Net 3.5 to run a legacy vendor app.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Convert-Wim2VHD -Path G:\vhd\2012r2_eval_gui.vhdx -Size 60gb -Dynamic -DiskLayout UEFI -SourcePath G:\iso\Srv2012r2Eval.ISO -Index 2 -Feature NetFx3
</code></pre></div></div>
<p>The -Feature command takes the feature names that would be understood by DISM, or Install-WindowsOptionalFeature</p>
<p>Creating a VHDX is a good start, but we want to automate this. for that we need an Unatten.xml. I will cover that in the next blog</p>David JonesBladeFireLight@outlook.comOne of the time consuming steps to deploying new VMs is the time spend managing Images and and applying patches. I’m not big on Golden images. I tend to use a fully patched VHDX or VMDK and let DSC handle the configuration and software. This is not the fastest, and at scale you need to create more then one image based on what saves the most time. (IIS, SQL, Exchange, etc…).Windows Image Tools \| Overview2016-03-16T16:48:59-05:002016-03-16T16:48:59-05:00https://www.bladefirelight.com/windowsimagetools/windows-image-tools-overview<h2 id="the-problem">The problem</h2>
<p>One of the time consuming steps to deploying new VMs is the time spend managing Images and and applying patches. I’m not big on Golden images. I tend to use a fully patched VHDX or VMDK and let DSC handle the configuration and software. This is not the fastest, and at scale you need to create more then one image based on what saves the most time. (IIS, SQL, Exchange, etc…).</p>
<h2 id="what-to-do-about-it">What to do about it</h2>
<p>I have spend some time putting together a number of scripts to automate this, but after the release of windows 10 and it’s built in access to the galaxy I decided to look at updating and packaging my script into something portable and share it.</p>
<h3 id="why-a-module-why-not-a-script">Why a Module, why not a script?</h3>
<p>Well scripts are great for automating a process, but not the best when you want to customize for your own process. Say you use a script on the <a href="https://www.powershellgallery.com/">PowerShell Gallery</a> and have customized it to fit your environment and business process. Then their is a bug fix posted to the gallery. You get the fun of adapting the changes into your modified code. This is time consuming, and the reason so many thing in production remain outdated and buggy. But if your using a customized controller script<sup id="fnref:ControlerSsript"><a href="#fn:ControlerSsript" class="footnote">1</a></sup> that relies on a module function, being able to use the newer release from the PowerShell Gallery is less painful.</p>
<p>For that reason I have chosen to use a module and include a function that creates an example controller script.</p>
<h2 id="functions-included-in-windowsimagetools">Functions included in WindowsImageTools</h2>
<p>Functions cover three different areas, Creating VHDX, Manipulating them, and Updates.</p>
<h3 id="create-vhdx">Create VHDX</h3>
<ul>
<li>Initialize-VHDPartition
<ul>
<li>Create a VHD(x) with the partitions structure appropriate for the target generation of a VM</li>
</ul>
</li>
<li>Set-VHDPartition
<ul>
<li>Populate the VHD(x) partitions with the content of an ISO or WIM</li>
</ul>
</li>
<li>Convert-Wim2VHD
<ul>
<li>Wrapper combining Initialize-VHDPartition and Set-VHDPartition in a single function</li>
</ul>
</li>
<li>New-UnattendXml
<ul>
<li>Create an Unattent.xml to silently setup windows</li>
</ul>
</li>
</ul>
<h3 id="work-with-vhdx">Work with VHDX</h3>
<ul>
<li>Get-VhdPartitionStyle
<ul>
<li>Returns GPT<sup id="fnref:GPT"><a href="#fn:GPT" class="footnote">2</a></sup> or MBR<sup id="fnref:MBR"><a href="#fn:MBR" class="footnote">3</a></sup></li>
</ul>
</li>
<li>Mount-VhdAndRunBlock
<ul>
<li>Mounts a VHD(x) sets the letter of the first mount point to $driveLeter and invokes a script block. useful for manipulating files inside a VHD</li>
</ul>
</li>
<li>Invoke-CreateVmRunAndWait
<ul>
<li>Created a VM attached the VHD to it and waits for it to stop. Used for running boot time scripts that shut down when finished.</li>
</ul>
</li>
</ul>
<h3 id="update-vhdx">Update VHDX</h3>
<ul>
<li>New-WindowsImageToolsExample
<ul>
<li>Creates an example folder structure and example controller scripts</li>
</ul>
</li>
<li>Get-UpdateConfig
<ul>
<li>Gets the update configuration stored in the update folders. Contains VM Switch, and IP addresses needed to access the internet when running updates.</li>
</ul>
</li>
<li>Set-UpdateConfig
<ul>
<li>Change settings in the update configuration</li>
</ul>
</li>
<li>Add-UpdateImage
<ul>
<li>Uses Convert-Wim2VHD and New-UnattendXml to prep a VHDX for updating</li>
</ul>
</li>
<li>Update-WindowsImageWMF
<ul>
<li>Update VHDX to WMF 4 or WMF 5</li>
</ul>
</li>
<li>Invoke-WindowsImageUpdate
<ul>
<li>For one VHDX or all VHD’s in update folder, run windows update and output WIM and optional VHDX</li>
</ul>
</li>
</ul>
<div class="footnotes">
<ol>
<li id="fn:ControlerSsript">
<p>A script that is specific to a business process <a href="#fnref:ControlerSsript" class="reversefootnote">↩</a></p>
</li>
<li id="fn:GPT">
<p>GUID Partition Table. Used for Generation 2 and UEFI. will contain MSR, UEFI and Primary partition, may include options Recovery Tools and Recovery Image partitions <a href="#fnref:GPT" class="reversefootnote">↩</a></p>
</li>
<li id="fn:MBR">
<p>Master Boot Record. Used for Generation 1 and legacy BIOS. Will use one Primary partition <a href="#fnref:MBR" class="reversefootnote">↩</a></p>
</li>
</ol>
</div>David JonesBladeFireLight@outlook.comOne of the time consuming steps to deploying new VMs is the time spend managing Images and and applying patches. I’m not big on Golden images. I tend to use a fully patched VHDX or VMDK and let DSC handle the configuration and software. This is not the fastest, and at scale you need to create more then one image based on what saves the most time. (IIS, SQL, Exchange, etc…).