function Create-HardwareProfile { [CmdletBinding()] Param( [ValidateNotNullOrEmpty()] [Parameter(Mandatory=$false, Position=1, HelpMessage='The number of CPUs in the new machine.')] [ValidateRange(1,4)] [int] $CPUCount = 1, [ValidateNotNullOrEmpty()] [Parameter(Mandatory=$false, Position=2, HelpMessage='The minimum amount of memory for the guest.')] [ValidateRange(128,65535)] [int] $MinMemoryMB = 1024, [ValidateNotNullOrEmpty()] [Parameter(Mandatory=$false, Position=3, HelpMessage='The maximum amount of memory for the guest.')] [ValidateRange(128,65535)] [int] $MaxMemoryMB = 4096, [Parameter(Mandatory=$false, Position=4, HelpMessage='A Logical Network Connection to associate with the new HW Profile.')] [Microsoft.SystemCenter.VirtualMachineManager.LogicalNetwork] $Network, [Parameter(Mandatory=$false, Position=5, HelpMessage='A Boolean indiciating that a DVD Drive should be included.')] [switch] $IncludeDVD, [Parameter(Mandatory=$false, Position=6, HelpMessage='Specifies an identifier for a series of commands that will run as a set.')] [System.Guid] $JobGroup = [System.Guid]::NewGuid() ) Begin {Write-Debug "$($MyInvocation.MyCommand.Name):: Function started"} Process { try { Write-Host "Creating Hardware Profile" $JobGroupID = $JobGroup.toString() $hwProfileName = "Temp Hardware Profile - $($JobGroupID)" if ($Network) { Write-Host "Adding Network adapter..." New-SCVirtualNetworkAdapter -VMMServer $VMMServerName -JobGroup $JobGroupID -MACAddressType Dynamic -LogicalNetwork $Network -Synthetic -EnableVMNetworkOptimization $false -EnableMACAddressSpoofing $false -IPv4AddressType Dynamic -IPv6AddressType Dynamic } if ($IncludeDVD) { Write-Host "Adding DVD drive..." New-SCVirtualDVDDrive -VMMServer $VMMServerName -JobGroup $JobGroupID -Bus 0 -LUN 1 } Set-SCVirtualCOMPort -NoAttach -VMMServer $VMMServerName -GuestPort 1 -JobGroup $JobGroupID Set-SCVirtualCOMPort -NoAttach -VMMServer $VMMServerName -GuestPort 2 -JobGroup $JobGroupID Set-SCVirtualFloppyDrive -RunAsynchronously -VMMServer $VMMServerName -NoMedia -JobGroup $JobGroupID New-SCVirtualScsiAdapter -VMMServer $VMMServerName -JobGroup $JobGroupID -AdapterID 255 -ShareVirtualScsiAdapter $false -ScsiControllerType DefaultTypeNoType Write-Host "Getting Capability Profile..." $CapabilityProfile = Get-SCCapabilityProfile -VMMServer $VMMServerName | where {$_.Name -eq "Hyper-V"} Write-Host "Creating Hardware Profile..." $HardwareProfile = New-SCHardwareProfile -VMMServer $VMMServerName -Name $hwProfileName -CPUCount $CPUCount -MemoryMB $MinMemoryMB -DynamicMemoryEnabled $true -DynamicMemoryMaximumMB $MaxMemoryMB -CapabilityProfile $CapabilityProfile -CPULimitFunctionality $false -CPULimitForMigration $true -JobGroup $JobGroupID return $HardwareProfile } catch { throw $_ } } End {Write-Debug "$($MyInvocation.MyCommand.Name):: Function ended"} } function Create-MachineTemplate { [CmdletBinding()] Param( [ValidateNotNullOrEmpty()] [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=0, HelpMessage='The name of the new template.')] [string] $TemplateName, [Parameter(Mandatory=$false, Position=1, HelpMessage='The Base Template to copy from.')] [Microsoft.SystemCenter.VirtualMachineManager.Template] $BaseTemplate, [ValidateNotNullOrEmpty()] [Parameter(Mandatory=$false, Position=2, HelpMessage='The Hardware Profile for the new machine.')] [Microsoft.SystemCenter.VirtualMachineManager.HardwareProfile] $HardwareProfile, [Parameter(Mandatory=$false, Position=3, HelpMessage='Specifies an identifier for a series of commands that will run as a set.')] [System.Guid] $JobGroup = [System.Guid]::NewGuid(), [Parameter(Mandatory=$false, Position=4, HelpMessage='An answer file for machine deployment.')] [Microsoft.SystemCenter.VirtualMachineManager.Script] $AnswerFile, [Parameter(Mandatory=$false, Position=5, HelpMessage='Specifies an application profile object.')] [Microsoft.SystemCenter.VirtualMachineManager.ApplicationProfile] $ApplicationProfile, [Parameter(Mandatory=$false, Position=6, HelpMessage='An array of commands to execute as part of the GUI Run Once Deployment of the machine.')] [String[]] $GUIRunOnceCommands, [Parameter(Mandatory=$false, Position=7, HelpMessage='A switch that will retain the Hardware Profle associated with this tempalte.')] [switch] $RetainHardwareProfile, [Parameter(Mandatory=$false, Position=8, HelpMessage='A switch that will retain the Application Profle associated with this tempalte.')] [switch] $RetainApplicationProfile, [Parameter(Mandatory=$false, Position=9, HelpMessage='An XML Element with the configuration for the machine.')] [System.Xml.XmlElement] $MachineConfiguration ) Begin {Write-Debug "$($MyInvocation.MyCommand.Name):: Function started"} Process { try { $existingTemplate = Get-SCVMTemplate -All | Where-Object { $_.Name -eq $TemplateName } if (!($existingTemplate)) { Write-Host "Creating New Template" $command = @("New-SCVMTemplate") $command += "-Name `$TemplateName" if ($BaseTemplate) { $command += "-VMTemplate `$BaseTemplate" } if ($HardwareProfile) { $command += "-HardwareProfile `$HardwareProfile" } if ($JobGroup) { $command += "-JobGroup `$JobGroup" } $command += "-ComputerName $($MachineConfiguration.Name)" $command += "-Workgroup `"WORKGROUP`"" if ($GUIRunOnceCommands) { $command += "-GuiRunOnceCommands `$GUIRunOnceCommands" } if ($AnswerFile) { $command += "-AnswerFile `$AnswerFile" } $command += "-MergeAnswerFile `$true" $command += "-OperatingSystem `$BaseTemplate.OperatingSystem" if ($ApplicationProfile) { $command += "-ApplicationProfile `$ApplicationProfile" } return Invoke-Expression -Command ([string]::join(" ", $command)) } return $existingTemplate } catch { throw $_ } finally { if (!$RetainHardwareProfile -and $HardwareProfile) { Write-Host "Removing Hardware Profile: $($HardwareProfile.Name)" Remove-SCHardwareProfile -HardwareProfile $HardwareProfile | Out-Null } if (!$RetainApplicationProfile -and $ApplicationProfile) { Write-Host "Removing Application Profile: $($ApplicationProfile.Name)" Remove-SCApplicationProfile -ApplicationProfile $ApplicationProfile | Out-Null } } } End {Write-Debug "$($MyInvocation.MyCommand.Name):: Function ended"} } function Create-ApplicationProfile { [CmdletBinding()] Param( [Parameter(Mandatory=$true, Position=0, HelpMessage='Specifies the name of an executable to run.')] [string] $Executable, [Parameter(Mandatory=$true, Position=1, HelpMessage='Specifies a script type.')] [Microsoft.VirtualManager.Remoting.ScriptCommandType] $ScriptType, [Parameter(Mandatory=$false, Position=2, HelpMessage='Parameters to pass to the executable.')] [string] $CommandParameters, [Parameter(Mandatory=$true, Position=3, HelpMessage='The Operating System.')] [Microsoft.SystemCenter.VirtualMachineManager.OperatingSystem] $OS, [Parameter(Mandatory=$false, Position=4, HelpMessage='Specifies an identifier for a series of commands that will run as a set.')] [System.Guid] $JobGroup = [System.Guid]::NewGuid() ) Begin {Write-Debug "$($MyInvocation.MyCommand.Name):: Function started"} Process { try { Write-Host "Creating Application Profile" $JobGroupID = $JobGroup.toString() $appProfileName = "Temp App Profile - $($JobGroupID)" $profile = New-SCApplicationProfile -Name $appProfileName Add-SCOperatingSystem -ApplicationProfile $profile -OperatingSystem $OS | Out-Null Add-SCScriptCommand -ApplicationProfile $profile -Executable $Executable -ScriptType $ScriptType -CommandParameters $CommandParameters -TimeoutSeconds 360000 | Out-Null return Get-SCApplicationProfile -ID $profile.ID } catch { throw $_ } } End {Write-Debug "$($MyInvocation.MyCommand.Name):: Function ended"} } function Create-NewVM { [CmdletBinding()] Param( [ValidateNotNullOrEmpty()] [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=0, HelpMessage='The template to build the machine from.')] [Microsoft.SystemCenter.VirtualMachineManager.Template] $Template, [ValidateNotNullOrEmpty()] [Parameter(ValueFromPipeline=$false, Mandatory=$true, Position=1, HelpMessage='The name of the new machine.')] [string] $ComputerName, [ValidateNotNullOrEmpty()] [Parameter(ValueFromPipeline=$false, Mandatory=$true, Position=2, HelpMessage='The Cloud to deploy the machine within.')] [Microsoft.SystemCenter.VirtualMachineManager.Cloud] $Cloud, [Parameter(Mandatory=$false, Position=3, HelpMessage='Specifies an identifier for a series of commands that will run as a set.')] [System.Guid] $JobGroup = [System.Guid]::NewGuid(), [Parameter(Mandatory=$false, Position=4, HelpMessage='A switch that will retain the template used during the machine creation.')] [switch] $RetainTemplate, [Microsoft.SystemCenter.VirtualMachineManager.Remoting.ServerConnection] $VMMServer ) Begin {Write-Debug "$($MyInvocation.MyCommand.Name):: Function started"} Process { try { $limit = 0 Write-Host "Creating New Machine" $JobGroupID = $JobGroup.toString() $VMConfig = New-SCVMConfiguration -VMTemplate $Template -Name $ComputerName $VM = New-SCVirtualMachine -Name $ComputerName -VMConfiguration $VMConfig -Cloud $cloud -Description "" -JobGroup $JobGroupID -RunAsynchronously -JobVariable "machineCreationJob" | Out-Null while ($machineCreationJob.Status -ne "Completed") { if ($machineCreationJob.Status -eq "Failed") { Receive-Job $machineCreationJob Write-Host "Attempting to Restart Failed Job" $machineCreationJob = (Restart-SCJob -Job $machineCreationJob) if ($limit -ge 3) { throw "Failed to create machine" } $limit ++ } if ($machineCreationJob.Status -eq "Canceled") { Write-Host "Canceled" return } $lastProgess = $machineCreationJob.Progress if ($machineCreationJob.Status -eq "Running" -and ($machineCreationJob.Progress -ne $lastProgess)) { $lastProgess = $machineCreationJob.Progress Write-Host "$($machineCreationJob.Status) - $($machineCreationJob.Progress)" } Sleep 15 $machineCreationJob = Get-SCJob -ID ($machineCreationJob.ID) -VMMServer $VMMServer } Write-Host "Finished!" return Get-SCVirtualMachine -ID ($machineCreationJob.ResultObjectID) throw "Failed to provision machine. $($machineCreationJob.ErrorInfo)" } catch { throw $_ } finally { if (!$RetainTemplate) { Write-Host "Removing VM Template: $($Template.Name)" Remove-SCVMTemplate -VMTemplate $Template | Out-Null } } } End {Write-Debug "$($MyInvocation.MyCommand.Name):: Function ended"} } function Power-On { [CmdletBinding()] Param( [ValidateNotNullOrEmpty()] [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=0, HelpMessage='The machine to power on.')] [Microsoft.SystemCenter.VirtualMachineManager.VM] $Machine ) Begin {Write-Debug "$($MyInvocation.MyCommand.Name):: Function started"} Process { Write-Host "Starting VM: $($Machine.Name)" Start-SCVirtualMachine -VM $Machine } End {Write-Debug "$($MyInvocation.MyCommand.Name):: Function ended"} } function Power-Off { [CmdletBinding()] Param( [ValidateNotNullOrEmpty()] [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=0, HelpMessage='The machine to power off.')] [Microsoft.SystemCenter.VirtualMachineManager.VM] $Machine ) Begin {Write-Debug "$($MyInvocation.MyCommand.Name):: Function started"} Process { Write-Host "Stopping VM: $($Machine.Name)" if ($Machine.Status -eq "Running") { Stop-SCVirtualMachine -VM $Machine } } End {Write-Debug "$($MyInvocation.MyCommand.Name):: Function ended"} } function Delete-Machine { [CmdletBinding()] Param( [ValidateNotNullOrEmpty()] [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=0, HelpMessage='The machine to delete.')] [Microsoft.SystemCenter.VirtualMachineManager.VM] $Machine ) Begin {Write-Debug "$($MyInvocation.MyCommand.Name):: Function started"} Process { Write-Host "Deleting VM: $($Machine.Name)" Remove-SCVirtualMachine -VM $Machine -Force } End {Write-Debug "$($MyInvocation.MyCommand.Name):: Function ended"} } function Update-DiskConfiguration { [CmdletBinding()] Param( [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=0, HelpMessage='The disk configuration XML Element.')] [System.Xml.XmlElement] $DriveConfiguration, [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=1, HelpMessage='The machine to modify.')] [Microsoft.SystemCenter.VirtualMachineManager.VM] $Machine ) Begin {Write-Debug "$($MyInvocation.MyCommand.Name):: Function started"} Process { # Drive Resizing and Additional Drive Assignments cannot be processed until # the machine has been provisioned. $DrivesToProcess = $DriveConfiguration.ChildNodes $private:JobGroup = [System.Guid]::NewGuid() $availableSCSILuns = (0..64) -ne 6 $availableIDELuns = (0..1) $Machine.VirtualDiskDrives | Where-Object { $_.BusType -eq "SCSI" } | Select "Lun" | ForEach-Object { $availableSCSILuns = $availableSCSILuns -ne $_.LUN } $Machine.VirtualDiskDrives | Where-Object { $_.BusType -eq "IDE" -and $_.Bus -eq 1 } | Select "Lun" | ForEach-Object { $availableIDELuns = $availableIDELuns -ne $_.LUN } foreach($Drive in $DrivesToProcess) { $command = $null if ($Machine) { if ($Drive.LocalName -eq "SystemDrive") { $result = Expand-Disk -Machine $Machine -Size $Drive.Size -JobGroup $JobGroup $command = $null } if ($Drive.LocalName -eq "Drive") { $command = @("New-SCVirtualDiskDrive", "-JobGroup `$JobGroup", "-Dynamic", "-VirtualHardDiskSizeMB $($Drive.Size)", "-VolumeType None", "-FileName `"$([System.Guid]::NewGuid())`"") if ($Drive.Interface -eq "SCSI") { if ($availableSCSILuns -ge 1) { $command += "-SCSI -BUS 0 -LUN $($availableSCSILuns[0])" $availableSCSILuns = $availableSCSILuns -ne $availableSCSILuns[0] } else { $command = $null } } if ($Drive.Interface -eq "IDE") { if ($availableIDELuns -ge 1) { $command += "-IDE -BUS 1 -LUN $($availableIDELuns[0])" $availableIDELuns = $availableIDELuns -ne $availableIDELuns[0] } else { $command = $null } } } } if ($command) { Write-Verbose ([string]::join(" ", $command)) Invoke-Expression -Command ([string]::join(" ", $command)) | Out-Null } } Set-SCVirtualMachine -VM $Machine -JobGroup $JobGroup } End {Write-Debug "$($MyInvocation.MyCommand.Name):: Function ended"} } function Expand-Disk { [CmdletBinding()] Param( [ValidateNotNullOrEmpty()] [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=0, HelpMessage='The machine to power on.')] [Microsoft.SystemCenter.VirtualMachineManager.VM] $Machine, [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=1, HelpMessage='The size of the drive in GB.')] [int] $Size, [Parameter(Mandatory=$false, Position=2, HelpMessage='Specifies an identifier for a series of commands that will run as a set.')] [System.Guid] $JobGroup = [System.Guid]::NewGuid() ) Begin {Write-Debug "$($MyInvocation.MyCommand.Name):: Function started"} Process { Write-Host "Expanding disk for VM: $($Machine.Name)" $Drive = Get-SCVirtualDiskDrive -VM $Machine | Select -First 1 if ($Drive -and ($Drive.Size -lt $Size)) { Expand-SCVirtualDiskDrive -JobGroup $JobGroup -VirtualDiskDrive $Drive -VirtualHardDiskSizeGB $Size -Verbose } return $? } End {Write-Debug "$($MyInvocation.MyCommand.Name):: Function ended"} } function Change-Owner { [CmdletBinding()] Param( [ValidateNotNullOrEmpty()] [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=0, HelpMessage='The machine to power on.')] [Microsoft.SystemCenter.VirtualMachineManager.VM] $Machine, [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=1, HelpMessage='The new owner of the machine.')] [string] $Owner, [Parameter(ValueFromPipeline=$true, Mandatory=$false, Position=2, HelpMessage='The user role for the new owner.')] [Microsoft.SystemCenter.VirtualMachineManager.CloudManagerUserRole] $Role ) Begin {Write-Debug "$($MyInvocation.MyCommand.Name):: Function started"} Process { Write-Host "Changing owner for VM: $($Machine.Name)" $command = @("Set-SCVirtualMachine -VM `$Machine -Owner `$Owner") if ($Role) { $command += "-UserRole `$Role" } Invoke-Expression -Command ([string]::join(" ", $command)) return $? } End {Write-Debug "$($MyInvocation.MyCommand.Name):: Function ended"} } function Post-Creation { [CmdletBinding()] Param( [ValidateNotNullOrEmpty()] [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=0, HelpMessage='The machine to power on.')] [Microsoft.SystemCenter.VirtualMachineManager.VM] $Machine, [Parameter(Mandatory=$true, Position=1, HelpMessage='An XML Element with the configuration for the machine.')] [System.Xml.XmlElement] $MachineConfiguration ) Begin {Write-Debug "$($MyInvocation.MyCommand.Name):: Function started"} Process { Write-Host "Post Creation for VM: $($Machine.Name)" Update-DiskConfiguration -Machine $Machine -DriveConfiguration $MachineConfiguration.Hardware.Drives $adminRole = Get-SCUserRole | Where-Object {$_.Members.Name -ccontains "DOMAIN\admins"} | Select -First 1 Change-Owner -Machine $Machine -Owner "DOMAIN\admins" -Role $adminRole | Out-Null Power-On -Machine $Machine | Out-Null } End {Write-Debug "$($MyInvocation.MyCommand.Name):: Function ended"} } function Create-MachineAsJob { # All Parameter attributes have been removed. These were causing a problem # when trying to call this function as a job. Param( [string] $TemplateName, [string] $CloudName, [string] $MachineConfig, [System.Guid] $JobGroup = [System.Guid]::NewGuid(), [string] $VMMServerName, [string] $VMLibrary ) Begin { Write-Output "Starting.." Write-Debug "$($MyInvocation.MyCommand.Name):: Function started"} Process { Write-Output "Loading VM Create Library" . $VMLibrary $private:Template = Get-SCVMTemplate -Name $TemplateName $private:Cloud = Get-SCCloud -Name $CloudName $private:VMMServer = Get-VMMServer -ComputerName $VMMServerName -SetAsDefault $private:MachineConfiguration = ([xml]$MachineConfig).Computer $private:VM = Create-NewVM -Template $Template -Cloud $Cloud -ComputerName $MachineConfiguration.Name -JobGroup $JobGroup -VMMServer $VMMServer if ($VM) { Post-Creation -Machine $VM -MachineConfiguration $MachineConfiguration } } End {Write-Debug "$($MyInvocation.MyCommand.Name):: Function ended"} } trap [System.Management.Automation.RuntimeException] { continue; } Write-Verbose "Loading Module VirtualMachineManager" Import-Module VirtualMachineManager -ErrorAction SilentlyContinue