DSC Resource: Create an unattend.xml file

Today, it’s a quick post to give you a custom dsc Resource of mine..

Bellow, here is a script that you have to reg in a *.psm1 file to enable you creating very basic unattend.xml file for your installations, it’s very helpfull when building your home lab, or other infras 🙂

Like always with DSC, here is how create it and use it 🙂

The unattend template file credit goes to @energizedtech from his technet hyper-v script 🙂

Using in a configuration

configuration Unattend { 

    Import-DscResource -module cUnattend
 
    Node localhost { 
        
        cUnattend Basic {
            Name = "srv01"
            Organization = "This is a test"
            Owner = "Fabien Dibot"
            Timezone = "Greenwich Standard Time"
            AdminPassword = "P4ssw0rd!"
            ProductKey = "Some-product_key"
            TargetPath = "G:\Scripts\unattend.XML"
            Ensure = "Present"
            TemplatePath = "C:\Program Files\WindowsPowerShell\Modules\cUnattend\DSCResources\cUnattend\unattend-template.xml"
        }

    } 
} 

Push-Location G:\Scripts
Unattend

 Building the resource

To build the skeleton, please use New-xDscResource cmdlet 😉

$TemplatePath = New-xDscResourceProperty -Name TemplatePath -Type String -Attribute Write
$Name = New-xDscResourceProperty -Name Name -Type String -Attribute Key
$Organization = New-xDscResourceProperty -Name Organization -Type String -Attribute Write
$Owner = New-xDscResourceProperty -Name Owner -Type String -Attribute Write
$TimeZone = New-xDscResourceProperty -Name TimeZone -Type String -Attribute Write
$AdminPassword = New-xDscResourceProperty -Name AdminPassword -Type String -Attribute Write
$ProductKey = New-xDscResourceProperty -Name ProductKey -Type String -Attribute Write
$Ensure = New-xDscResourceProperty -Name Ensure -Type String -Attribute Write -ValidateSet "Present", "Absent"
$TargetPath = New-xDscResourceProperty -Name TargetPath -Type String -Attribute Write 

#Now create the resource
New-xDscResource -Name cUnattend -Property $Name,$TemplatePath,$Organization,$Owner,$TimeZone,$AdminPassword,$ProductKey,$Ensure,$TargetPath  -Path 'C:\Program Files\WindowsPowerShell\Modules\cUnattend'

Once the modules directory created pleas put a psd1 file in the module root directory -> C:\Program Files\WindowsPowerShell\Modules\cUnattend

@{
# Version number of this module.
ModuleVersion = '1.0'

# ID used to uniquely identify this module
GUID = '5106df04-d187-462a-9baf-89bea7babc05'

# Author of this module
Author = 'Fabien Dibot'

# Description of the functionality provided by this module
Description = 'Module with DSC Resources for creating unattended files'

# Minimum version of the Windows PowerShell engine required by this module
PowerShellVersion = '5.0'

# Functions to export from this module
FunctionsToExport = '*'

# Cmdlets to export from this module
CmdletsToExport = '*'
}

Overwrite the psm1 file by this code:

function Get-TargetResource
{
	[CmdletBinding()]
	[OutputType([System.Collections.Hashtable])]
	param
	(
        [parameter(Mandatory = $true)]
		[System.String]
		$Name,
        
		[System.String]
		$TargetPath
        
	)

	Write-Verbose "Collecting datas from the unattend file"

    if (Test-Path $TargetPath) {
        [XML]$Unattend = Get-Content -LiteralPath $TargetPath
	    $returnValue = @{
		    TargetPath = $TargetPath
		    Name = $Unattend.unattend.settings.component[0].ComputerName
		    Organization = $Unattend.unattend.settings.component[0].RegisteredOrganization
		    Owner = $Unattend.unattend.settings.component[0].RegisteredOwner
		    TimeZone = $Unattend.unattend.settings.component[0].TimeZone
		    AdminPassword = $Unattend.unattend.settings.component[1].AutoLogon.Password.Value
		    ProductKey = $Unattend.unattend.settings.component[0].ProductKey
		    Ensure = "Present"
	    }

	    $returnValue

    }
    else {
        Write-Verbose "$TargetPath not present on system."
    }

	
}


function Set-TargetResource
{
	[CmdletBinding()]
	param	(
		[System.String]
		$TemplatePath = "C:\Program Files\WindowsPowerShell\Modules\cUnattend\DSCResources\cUnattend\Unattend-template.xml",

		[parameter(Mandatory = $true)]
		[System.String]
		$Name,

		[System.String]
		$Organization,

		[System.String]
		$Owner,

		[ValidateSet("Yakutsk Standard Time",
                     "West Pacific Standard Time",
                     "West Asia Standard Time",
                     "W. Europe Standard Time",
                     "W. Central Africa Standard Time",
                     "W. Australia Standard Time",
                     "Vladivostok Standard Time",
                     "US Mountain Standard Time",
                     "US Eastern Standard Time",
                     "Tonga Standard Time",
                     "Tokyo Standard Time",
                     "Tasmania Standard Time",
                     "Taipei Standard Time",
                     "Sri Lanka Standard Time",
                     "South Africa Standard Time",
                     "Singapore Standard Time",
                     "SE Asia Standard Time",
                     "Samoa Standard Time",
                     "SA Western Standard Time",
                     "SA Pacific Standard Time",
                     "SA Eastern Standard Time",
                     "Russian Standard Time",
                     "Romance Standard Time",
                     "Pacific Standard Time",
                     "Pacific SA Standard Time",
                     "North Asia Standard Time",
                     "North Asia East Standard Time",
                     "Newfoundland Standard Time",
                     "New Zealand Standard Time",
                     "Nepal Standard Time",
                     "Namibia Standard Time",
                     "N. Central Asia Standard Time",
                     "Myanmar Standard Time",
                     "Mountain Standard Time (Mexico)",
                     "Mountain Standard Time",
                     "Mid-Atlantic Standard Time",
                     "Korea Standard Time",
                     "Israel Standard Time",
                     "Iran Standard Time",
                     "India Standard Time",
                     "Hawaiian Standard Time",
                     "GTB Standard Time",
                     "Greenwich Standard Time",
                     "Greenland Standard Time",
                     "GMT Standard Time",
                     "Georgian Standard Time",
                     "FLE Standard Time",
                     "Fiji Standard Time",
                     "Ekaterinburg Standard Time",
                     "Egypt Standard Time",
                     "Eastern Standard Time",
                     "E. South America Standard Time",
                     "E. Europe Standard Time",
                     "E. Australia Standard Time",
                     "E. Africa Standard Time",
                     "Dateline Standard Time",
                     "China Standard Time",
                     "Central Standard Time (Mexico)",
                     "Central Standard Time",
                     "Central Pacific Standard Time",
                     "Central European Standard Time",
                     "Central Europe Standard Time",
                     "Central Brazilian Standard Time",
                     "Central Asia Standard Time",
                     "Central America Standard Time",
                     "Cen. Australia Standard Time",
                     "Caucasus Standard Time",
                     "Cape Verde Standard Time",
                     "Canada Central Standard Time",
                     "Azores Standard Time",
                     "Azerbaijan Standard Time",
                     "AUS Eastern Standard Time",
                     "AUS Central Standard Time",
                     "Atlantic Standard Time",
                     "Arabic Standard Time",
                     "Arabian Standard Time",
                     "Arab Standard Time",
                     "Alaskan Standard Time",
                     "Afghanistan Standard Time",
                     "Yakutsk Standard Time")]
		[System.String]
		$TimeZone,

		[System.String]
		$AdminPassword,

		[System.String]
		$ProductKey,

		[System.String]
		$TargetPath,

        [ValidateSet("Present","Absent")]
		[System.String]
		$Ensure

	)

	Write-Verbose "Creating/removing unattend.XML file"

    switch ($ensure) {
        "Present" {
            Try {
                $Unattend = New-Object XML
                $Unattend.Load($TemplatePath)

                $Unattend.unattend.settings.component[0].ComputerName                             = $Name
                $Unattend.unattend.settings.component[0].ProductKey                               = $ProductKey
                $Unattend.unattend.settings.component[0].RegisteredOrganization                   = $Organization
                $Unattend.unattend.settings.component[0].RegisteredOwner                          = $Owner
                $Unattend.unattend.settings.component[0].TimeZone                                 = $TimeZone
                $Unattend.unattend.settings.Component[1].RegisteredOrganization                   = $Organization
                $Unattend.unattend.settings.Component[1].RegisteredOwner                          = $Owner
                $Unattend.unattend.settings.component[1].UserAccounts.AdministratorPassword.Value = $AdminPassword
                $Unattend.unattend.settings.component[1].autologon.password.value                 = $AdminPassword

                $Unattend.save($TargetPath)
                Write-Verbose "$TargetPath Created."
            }
            Catch {
                throw $_.Exception.Message
            }
        }
        "Absent" {
            if (Test-Path $TargetPath) {
                Remove-Item $TargetPath -force
                Write-Verbose "$TargetPath removed."
            }
            else {
                Write-Verbose "$TargetPath already removed."
            }
        }
    }
}


function Test-TargetResource
{
	[CmdletBinding()]
	[OutputType([System.Boolean])]
	param
	(

		[parameter(Mandatory = $true)]
		[System.String]
		$Name,


		[System.String]
		$Organization,


		[System.String]
		$Owner,

		[System.String]
		$TimeZone,


		[System.String]
		$AdminPassword,

		[System.String]
		$ProductKey,

		[ValidateSet("Present","Absent")]
		[System.String]
		$Ensure,

		[System.String]
		$TargetPath,

        [System.String]
		$TemplatePath = ""
	)

	Write-Verbose "Testing values in"

	switch ($ensure) {
        "Present" {
            if (Test-Path $TargetPath) {
                [XML]$Unattend = Get-Content -LiteralPath $TargetPath
                $return = $false
		        if ($Name -eq $Unattend.unattend.settings.component[0].ComputerName) {
		            if ($Organization -eq $Unattend.unattend.settings.component[0].RegisteredOrganization) {
		                if ($Owner -eq $Unattend.unattend.settings.component[0].RegisteredOwner) {
		                    if ($TimeZone -eq $Unattend.unattend.settings.component[0].TimeZone) {
		                        if ($AdminPassword -eq $Unattend.unattend.settings.component[1].AutoLogon.Password.Value) {
		                            if ($ProductKey -eq $Unattend.unattend.settings.component[0].ProductKey) {
                                        $return = $true
                                    }
                                }
                            }
                        }
		            }
                }
            }
            else {
                $return = $false
            }
        }
        "Absent" {
            if (Test-Path $TargetPath) {
                $return = $false
            }
            else {
                $return = $true
            }
        }
    }
    $return
}


Export-ModuleMember -Function *-TargetResource

<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
    <settings pass="specialize">
        <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <ComputerName>**ComputerName***</ComputerName>
            <ProductKey>***ProductKey***</ProductKey>
            <RegisteredOrganization>***RegOrg***</RegisteredOrganization>
            <RegisteredOwner>***RegOwner***</RegisteredOwner>
            <TimeZone>***TimeZone***</TimeZone>
        </component>
    </settings>
    <settings pass="oobeSystem">
        <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <UserAccounts>
                <AdministratorPassword>
                    <Value>***Password***</Value>
                    <PlainText>true</PlainText>
                </AdministratorPassword>
            </UserAccounts>
	      <AutoLogon>
	            <Password>
	              <Value>***Password***</Value>
	              <PlainText>true</PlainText>
	           </Password>
	        <Username>administrator</Username>
	        <LogonCount>2</LogonCount>
	        <Enabled>true</Enabled>
	      </AutoLogon>
            <RegisteredOrganization>***RegOrg***</RegisteredOrganization>
            <RegisteredOwner>***RegOwner***</RegisteredOwner>
            <OOBE>
                <HideEULAPage>true</HideEULAPage>
                <SkipMachineOOBE>true</SkipMachineOOBE>
            </OOBE>
        </component>
    </settings>
    <cpi:offlineImage cpi:source="" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
</unattend>

This XML must be located somewhere accesible by your node, it can also stored in the module file, but you know, i’m lazy…

You can find all informations here to recreate the module if you need more details about how create your resource.