Create custom Nano server 2016 VHD

Create nano server

I built this script so people easily can get a start and try their Nano server 2016 within a couple of minutes. I have only tried it with Windows server 2016 Technical preview 4. It is pretty straight forward the script launches a windows where you can name your server and check the functions you want, add the disk image och create the server:

1. Enter namne of VHD
2. Check the functions you want to add and press button add server, you can add multiple server if you want to.
3. Press button Add Disk Image to point out windows 2016 iso.
4. Press button create servers to start building VHDs.
5. Enter Administrator password
6. You ne VHD will be stored in C:\Temp_NanoServer

I have not tried all functions, hopefully they all work.

########################################################################################################
#Name: Create Nano Vhd.ps1
#Author: Viktor Lindström
#
#Comments:
#Creates custom Nano VHD from Windows 2016 ISO. I have only tried it with Technical preview 4.
#You have to run the script as administrator.
#Sometimes the script stop on "dismounting image..." this is microsofts script that stops and the vhd is created att you can stop the script manually.
#1. Enter namne of VHD
#2. Check the functions you want to add and press button add server, you can add multiple server if you want to.
#3. Press button Add Disk Image to point out windows 2016 iso.
#4. Press button create servers to start building VHDs.
#5. Enter Administrator password
#6. You ne VHD will be stored in C:\Temp_NanoServer
########################################################################################################




#build GUI


[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')

[xml]$XAML = @'

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:create_nano_server"
        Title="Create Nano Server" Height="483.193" Width="705.901">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="133*"/>
            <RowDefinition Height="319*"/>
        </Grid.RowDefinitions>
        <Label Name="labelComputername" Content="Computer name:" HorizontalAlignment="Left" Margin="24,28,0,0" VerticalAlignment="Top" Width="111" Height="26"/>
        <Label Name="labelFeaturesAndRoles" Content="Feature and roles:" HorizontalAlignment="Left" Margin="24,73,0,0" VerticalAlignment="Top" FontWeight="Bold" Height="26" Width="111"/>
        <CheckBox Name="checkBoxHyper" Content="Hyper-V role" HorizontalAlignment="Left" Margin="33,108,0,0" VerticalAlignment="Top" Height="15" Width="88"/>
        <CheckBox Name="checkBoxFailover" Content="Failover Clustering" HorizontalAlignment="Left" Margin="33,128,0,0" VerticalAlignment="Top" Height="15" Width="117" Grid.RowSpan="2"/>
        <CheckBox Name="checkBoxFile" Content="File Server role and other storage components" HorizontalAlignment="Left" Margin="33,35,0,0" VerticalAlignment="Top" Grid.Row="1" Height="15" Width="265"/>
        <CheckBox Name="checkBoxWindowsDefender" Content="Windows Defender Antimalware" HorizontalAlignment="Left" Margin="33,54,0,0" VerticalAlignment="Top" Grid.Row="1" Height="15" Width="190"/>
        <CheckBox Name="checkBoxReverse" Content="Reverse forwarders for application compatibility" HorizontalAlignment="Left" Margin="33,75,0,0" VerticalAlignment="Top" Grid.Row="1" Height="15" Width="272"/>
        <CheckBox Name="checkBoxDns" Content="DNS Server role" HorizontalAlignment="Left" Margin="33,95,0,0" VerticalAlignment="Top" Grid.Row="1" Height="15" Width="104"/>
        <CheckBox Name="checkBoxDcs" Content="Desired State Configuration (DSC)" HorizontalAlignment="Left" Margin="33,115,0,0" VerticalAlignment="Top" Grid.Row="1" Height="15" Width="199"/>
        <CheckBox Name="checkBoxIis" Content="Internet Information Server (IIS)" HorizontalAlignment="Left" Margin="33,135,0,0" VerticalAlignment="Top" Grid.Row="1" Height="15" Width="187"/>
        <CheckBox Name="checkBoxContainers" Content="Host support for Windows Containers" HorizontalAlignment="Left" Margin="33,155,0,0" VerticalAlignment="Top" Grid.Row="1" Height="15" Width="220"/>
        <CheckBox Name="checkBoxSystemCenter" Content="System Center Virtual Machine Manager agent" HorizontalAlignment="Left" Margin="33,175,0,0" VerticalAlignment="Top" Grid.Row="1" Height="15" Width="266"/>
        <CheckBox Name="checkBoxNpds" Content="Network Performance Diagnostics Service (NPDS)" HorizontalAlignment="Left" Margin="33,195,0,0" VerticalAlignment="Top" Grid.Row="1" Height="15" Width="281"/>
        <CheckBox Name="checkBoxDcb" Content="Data Center Bridging" HorizontalAlignment="Left" Margin="33,15,0,0" VerticalAlignment="Top" Grid.Row="1" Height="15" Width="389"/>
        <TextBox Name="textBoxComputerName" HorizontalAlignment="Left" Height="23" Margin="133,32,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
        <Label Name="labelServers" Content="Servers:" HorizontalAlignment="Left" Margin="382,28,0,0" VerticalAlignment="Top" Height="26" Width="51"/>
        <ListBox Name="listBox" HorizontalAlignment="Left" Height="332" Margin="456,32,0,0" Grid.RowSpan="2" VerticalAlignment="Top" Width="227"/>
        <Button Name="buttonAddServer" Content="Add Server" HorizontalAlignment="Left" Margin="33,247,0,0" Grid.Row="1" VerticalAlignment="Top" Width="75" Height="20"/>
        <Button Name="buttonCreateServers" Content="Create Servers" HorizontalAlignment="Left" Margin="456,276,0,0" Grid.Row="1" VerticalAlignment="Top" Width="99" Height="20"/>
        <Button Name="buttonAddIso" Content="Add Disk Image" HorizontalAlignment="Left" Margin="456,247,0,0" Grid.Row="1" VerticalAlignment="Top" Width="99" RenderTransformOrigin="0.5,0.5"/>
        <Label Name="labelAddDiskImage" Content="" HorizontalAlignment="Left" Margin="107,244,0,0" Grid.Row="1" VerticalAlignment="Top" Width="312"/>

    </Grid>
</Window>
'@

$reader=(New-Object System.Xml.XmlNodeReader $xaml) 
try{$Form=[Windows.Markup.XamlReader]::Load( $reader )}
catch{Write-Host "Unable to load Windows.Markup.XamlReader. Some possible causes for this problem include: .NET Framework is missing PowerShell must be launched with PowerShell -sta, invalid XAML code was encountered."; exit}
$xaml.SelectNodes("//*[@Name]") | foreach {Set-Variable -Name ($_.Name) -Value $Form.FindName($_.Name)}


############################################################################################################
$tempPath = "C:\Temp_NanoServer"
$hyperV = "-Compute "
$clustering = "-Clustering "
$oemDrivers = "-OEMDrivers "
$fileServer = "-Storage "
$defender = "-Defender "
$reverseForwarders = "-ReverseForwarders "
$dnsServer = "-Packages Microsoft-NanoServer-DNS-Package "
$dsc = "-Packages Microsoft-NanoServer-DSC-Package "
$iis = "-Packages Microsoft-NanoServer-IIS-Package "
$containers = "-Containers "
$sccm = "-Packages Microsoft-Windows-Server-SCVMM-Package -Packages Microsoft-Windows-Server-SCVMM-Compute-Package " 
$npds = "-Packages Microsoft-NanoServer-NPDS-Package "
$dcb = "-Packages Microsoft-NanoServer-DCB-Package "
$ipAddress = "-Ipv4Address "
$subnetMask = "-Ipv4SubnetMask "
$gateway = "-Ipv4Gateway "
$azure = "-ForAzure "




# Show an Open File Dialog and return the file selected by the user.
function Read-OpenFileDialog([string]$WindowTitle, [string]$InitialDirectory, [string]$Filter = "All files (*.*)|*.*", [switch]$AllowMultiSelect)
{  
    Add-Type -AssemblyName System.Windows.Forms
    $openFileDialog = New-Object System.Windows.Forms.OpenFileDialog
    $openFileDialog.Title = $WindowTitle
    if (![string]::IsNullOrWhiteSpace($InitialDirectory)) { $openFileDialog.InitialDirectory = $InitialDirectory }
    $openFileDialog.Filter = $Filter
    if ($AllowMultiSelect) { $openFileDialog.MultiSelect = $true }
    $openFileDialog.ShowHelp = $true    # Without this line the ShowDialog() function may hang depending on system configuration and running from console vs. ISE.
    $openFileDialog.ShowDialog() > $null
    if ($AllowMultiSelect) { return $openFileDialog.Filenames } else { return $openFileDialog.Filename }
}


$servers = @()

        $buttonAddServer.add_click({ 

        if ($checkBoxHyper.IsChecked) {$arguments += $hyperV}
        if ($checkBoxFailover.IsChecked) {$arguments += $clustering}
        if ($checkBoxBasic.IsChecked) {$arguments += $oemDrivers}
        if ($checkBoxFile.IsChecked) {$arguments += $fileServer}
        if ($checkBoxWindowsDefender.IsChecked) {$arguments += $defender}
        if ($checkBoxReverse.IsChecked) {$arguments += $reverseForwarders}
        if ($checkBoxDns.IsChecked) {$arguments += $dnsServer}
        if ($checkBoxDcs.IsChecked) {$arguments += $dsc}
        if ($checkBoxIis.IsChecked) {$arguments += $iis}
        if ($checkBoxContainers.IsChecked) {$arguments += $containers}
        if ($checkBoxSystemCenter.IsChecked) {$arguments += $sccm}
        if ($checkBoxNpds.IsChecked) {$arguments += $npds}
        if ($checkBoxDcb.IsChecked) {$arguments += $dcb}

            $Global:servers += New-Object psobject -Property @{
                        'Servername' = $textBoxComputerName.Text
                        'arguments' = $arguments
                        }
                        
                        $listServerArguments = $textBoxComputerName.Text + " " + $arguments
                        $listBox.Items.Add($listServerArguments)
                       if ($Variable)
                        {
                        Clear-Variable -Name arguments
                        }
})
            $buttonAddIso.add_click({

            $Global:imagePath = Read-OpenFileDialog -WindowTitle "Select Windows 2016 ISO" -InitialDirectory 'C:\' -Filter "ISO files (*.iso)|*.iso"
            if (![string]::IsNullOrEmpty($imagePath)) { $labelAddDiskImage.Content = $imagePath }
            else { "You did not select a file." }
           
            
            })
                
                $buttonCreateServers.add_click({

                    # Mount disk image.
                    Mount-DiskImage -ImagePath $imagePath 
                    
                    # Get driveletter of mounted diskimage.
                    $driveLetterIso = (Get-DiskImage -ImagePath $imagePath | Get-Volume).DriveLetter + ":\"
                    
                    # Path to Nano Server folder on image.
                    $pathNanoServer = $driveLetterIso + "NanoServer"

                    
                    if (-Not(Test-Path "C:\temp_NanoServer\Convert-WindowsImage.ps1"))
                    {
                    # Create temp path to place scripts from nanoserverfolder.
                    New-Item -ItemType Directory -Path $tempPath
                    # Copy scripts from disk image to temp folder.
                    Copy-Item -Path ($pathNanoServer + "\*.ps*") -Destination $tempPath
                    }
                        
                        # Test if user is running powershell as administrator
                        function Test-Administrator  
                            {  
                            $user = [Security.Principal.WindowsIdentity]::GetCurrent();
                            (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)  
                            }

                                if (Test-Administrator)
                                {
                                    Set-Location $tempPath
                                    Import-Module .\NanoServerImageGenerator.psm1 -Verbose
                                  
                                }
                                else
                                {
                                Write-Host "You are not running powershell as administrator" -ErrorAction Stop -ForegroundColor Red -BackgroundColor Yellow
                                $labelAddDiskImage.Content = "You are not running powershell as administrator"
                                return
                                }
                                
            foreach ($server in $servers)
            {
                # create the VHD
                $targetPath = ".\nanoServerVM\" +$server.Servername + ".vhd"
                $commandNewNanoServer = "New-NanoServerImage -MediaPath $driveLetterIso -BasePath .\Base -TargetPath $targetPath -ComputerName $($server.Servername) $($server.arguments) -GuestDrivers -EnableRemoteManagementPort -Language en-US"
                Invoke-Expression -Command $commandNewNanoServer
          
            }
             })

$Form.ShowDialog() | out-null

First help desk GUI tool

helpdeskDelivered my first GUI tool to our help desk today. They needed a tool to find out which users populated distribution groups and they also wanted to be able to export three different attributes to a csv. I did the GUI design in visual studio and used XAML because it looks a little nicer then the old windows forms. the application got real-time check against the Active directory as you type to check if the group exist and it doesn´t enable the list user(Lista användare) button until you type the name of a group that exist. When the listing is done you can choose which attributes to export. This is a 1.0 and it doesn’t handle nested groups, if they want that functionality or some other functionality in the future I will update this post.

 

<#

.SYNOPSIS

Få reda på och exportera vilka medlemmar en AD-grupp har.



.DESCRIPTION

Kör skriptet och fyll grupp, applikationen kollar så att gruppen finns i AD:et annars går det inte att söka. Vid export bockar man i vilka attribut 
man vill exportera och trycker på exportknappen, det skapas då en fil under c:\temp som heter resultat[timme.minut.sekund].csv tex c:\temp\resultat10.30.21.csv.

.NOTES
Made by: Viktor Lindström
http://powerhell.nu
Version 1.0 fyll på med förändringar här under.
#>

#build GUI

[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')

[xml]$XAML = @'

<Window 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Medlemmar i AD-grupp" Height="350" Width="687.807">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="258*"/>
            <ColumnDefinition Width="259*"/>
        </Grid.ColumnDefinitions>
        <TextBox Name="textBox" HorizontalAlignment="Left" Height="23" Margin="99,28,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="230"/>
        <Label Name="LabelGruppnamn" Content="Gruppnamn:" HorizontalAlignment="Left" Margin="10,25,0,0" VerticalAlignment="Top" Width="76" RenderTransformOrigin="0.005,0.483"/>
        <ListBox Name="ListboxResult" Grid.Column="1" HorizontalAlignment="Left" Height="202" Margin="32,70,0,0" VerticalAlignment="Top" Width="279"/>
        <Button Name="ButtonSearch" Content="Lista användare" HorizontalAlignment="Left" Margin="99,70,0,0" VerticalAlignment="Top" Width="230" Height="35"/>
        <Label Name="LabelAnvändare" Content="Användare i gruppen:" Grid.Column="1" HorizontalAlignment="Left" Margin="185,25,0,0" VerticalAlignment="Top" Width="126"/>
        <CheckBox Name="checkBoxAnvändarnamn" Content="Användarnamn" HorizontalAlignment="Left" Margin="99,143,0,0" VerticalAlignment="Top" Width="240"/>
        <CheckBox Name="checkBoxNamn" Content="Namn" HorizontalAlignment="Left" Margin="99,169,0,0" VerticalAlignment="Top" Width="240"/>
        <CheckBox Name="checkBoxMail" Content="E-mailadress" HorizontalAlignment="Left" Margin="99,195,0,0" VerticalAlignment="Top" Width="240"/>
        <Label Name="labelExport" Content="Vad ska exporteras?" HorizontalAlignment="Left" Margin="10,117,0,0" VerticalAlignment="Top" Width="116"/>
        <Button Name="buttonExport" Content="Exportera" HorizontalAlignment="Left" Margin="99,230,0,0" VerticalAlignment="Top" Width="230" Height="31"/>
        <CheckBox Name="checkboxGruppnamn" Content="Grupp finns?" Grid.Column="1" HorizontalAlignment="Left" Margin="32,31,0,0" VerticalAlignment="Top"/>
        <Label Name="labelExport1" Content="" HorizontalAlignment="Left" Margin="10,279,0,0" VerticalAlignment="Top" Width="319"/>

    </Grid>
</Window>


'@

$reader=(New-Object System.Xml.XmlNodeReader $xaml) 
try{$Form=[Windows.Markup.XamlReader]::Load( $reader )}
catch{Write-Host "Unable to load Windows.Markup.XamlReader. Some possible causes for this problem include: .NET Framework is missing PowerShell must be launched with PowerShell -sta, invalid XAML code was encountered."; exit}
$xaml.SelectNodes("//*[@Name]") | foreach {Set-Variable -Name ($_.Name) -Value $Form.FindName($_.Name)}

# Global variable for result
$resultArray = @()

# Disable button by default
$ButtonSearch.IsEnabled = $false


# Funtion to test if AD-group exists.
function Test-Group 
{

if ((Get-ADGroup -Filter {SamAccountName -eq $textBox.Text}) -ne $null)
{$checkboxGruppnamn.IsChecked = $true
  $ButtonSearch.IsEnabled = $true} else {$checkboxGruppnamn.IsChecked = $false
  $ButtonSearch.IsEnabled = $false}
    }

# Searchbutton, gets all memebers of the group specified in the textbox, puts result in $resultarray
$ButtonSearch.add_click({
    $script:resultArray = @()
    $ListboxResult.Items.Clear()
    $allMembers = Get-ADGroupMember -Identity $textBox.Text
    $members = $allMembers | Where-Object {$_.objectClass -eq "user"}
    $groups = $allMembers | Where-Object {$_.objectClass -eq "group"}

    
        foreach ($item in $members)
        {
         $member = Get-ADObject -Identity $item -Properties mail, displayname, samaccountname | Select-Object samaccountname, mail, displayname
         $ListboxResult.Items.Add($member.displayname)
         $script:resultArray += $member
        }
          foreach ($group in $groups)
          {
              $ListboxResult.Items.Add($group.name)
              $script:resultArray += $group | Select-Object name
          }
                        })

# Exportbutton, exports attributes that has been checked in the checkboxes, exports to an CSV-file c:\temp\resultat[HH.mm.ss].csv to uniq it.
                $buttonExport.add_click({
                $export = @()
                
                $date = get-date -Format HH.mm.ss
                $exportdate =  "c:\temp\resultat$date.csv"
                if (Test-Path $exportdate) {Remove-Item $exportdate}

                    foreach ($object in $resultArray)
                    {
                     $export += New-Object psobject -Property @{
               
                      "Användarnamn" = if ($checkBoxAnvändarnamn.IsChecked -eq $true) {$object.samaccountname};
                      "Namn" = if ($checkBoxNamn.IsChecked -eq $true) {$object.displayname};
                      "E-mailadress" = if ($checkBoxMail.IsChecked -eq $true) {$object.mail};
                    }
                    }

                    $export | Export-Csv $exportdate -NoTypeInformation -Encoding UTF8 -Delimiter ";"
                    $labelExport1.Content = "Export klar, filen hittar du här: $exportdate"
                    

                    
                 })
# Runs the AD-test group function in realtime as you write in the textbox.
$textBox.add_Keyup({ 
    
    Test-Group
})

$Form.ShowDialog() | out-null