├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── dependabot.yml └── workflows │ └── powershell-analysis.yml ├── .gitignore ├── AD-DNS ├── Demote-DomainController.ps1 ├── Export-ADOUs.ps1 ├── Get-FsmoRoles.ps1 ├── Move-FsmoRoles.ps1 ├── Out-DnsZone.ps1 ├── Promote-DomainController.ps1 ├── README.md └── Restore-DnsZone.ps1 ├── HyperV ├── Delete-VM.ps1 ├── Get-HvMem.ps1 ├── Get-HyperVUUID.ps1 ├── Get-HyperVVMIPs.ps1 ├── Get-Memory-Notes.ps1 ├── GetVMUptime.ps1.txt ├── HyperV-ControlledMDTDeployment.ps1 ├── New-LabVM.ps1 ├── README.md ├── Set-Power.ps1 ├── Snippets-HyperV.ps1 ├── Start-SequentialVMs.ps1 ├── Test-New-LabVM.ps1 └── Working-Set-HypervDefaults.ps1 ├── LICENSE ├── Network ├── Set-WindowsFirewall.ps1 ├── Snippets-Networking.ps1 └── ipconfig.ps1 ├── README.md ├── StorageSpaces └── Config-StorageSpaces.ps1 ├── Tests ├── PesterOutput.PNG ├── README.md ├── RunTests.ps1 └── WindowsImage.Tests.ps1 ├── Various ├── Get-AvailableModule.ps1 ├── Get-CommandArguments.ps1 ├── Remove-ItemsByAge.ps1 └── Snippets.ps1 └── vSphere ├── BootVMs.ps1 ├── Delete-vSphereVM.ps1 ├── Get-vSphereVMUUID.ps1 ├── New-vSphereVM.ps1 ├── README.md ├── Set-ESXi-PowerPolicy.ps1 ├── vSphere-ControlledMDTDeployment.ps1 ├── vSphere-ControlledMDTDeployment.txt └── vSphere-ControlledMDTDeployment_v2.ps1 /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at aaron@stealthpuppy.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Set update schedule for GitHub Actions 2 | version: 2 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | # Check for updates to GitHub Actions every weekday 8 | interval: "daily" 9 | -------------------------------------------------------------------------------- /.github/workflows/powershell-analysis.yml: -------------------------------------------------------------------------------- 1 | name: PSScriptAnalyzer 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths: 7 | - '**.ps1' 8 | pull_request: 9 | branches: [ main ] 10 | paths: 11 | - '**.ps1' 12 | 13 | jobs: 14 | build: 15 | name: PSScriptAnalyzer 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: Run PSScriptAnalyzer 21 | uses: microsoft/psscriptanalyzer-action@6b2948b1944407914a58661c49941824d149734f 22 | with: 23 | # Check https://github.com/microsoft/action-psscriptanalyzer for more info about the options. 24 | # The below set up runs PSScriptAnalyzer to your entire repository and runs some basic security rules. 25 | path: ./ 26 | recurse: true 27 | # Include your own basic security rules. Removing this option will run all the rules 28 | #includeRule: '"PSAvoidGlobalAliases", "PSAvoidUsingConvertToSecureStringWithPlainText"' 29 | output: results.sarif 30 | 31 | # Upload the SARIF file generated in the previous step 32 | - name: Upload SARIF results file 33 | uses: github/codeql-action/upload-sarif@v3 34 | with: 35 | sarif_file: results.sarif 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db 38 | Redists.xml 39 | -------------------------------------------------------------------------------- /AD-DNS/Demote-DomainController.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # Windows PowerShell script for AD DS Deployment 3 | # 4 | 5 | Import-Module ADDSDeployment 6 | Uninstall-ADDSDomainController ` 7 | -DemoteOperationMasterRole:$true ` 8 | -DnsDelegationRemovalCredential (Get-Credential) ` 9 | -IgnoreLastDnsServerForZone:$true ` 10 | -RemoveDnsDelegation:$true ` 11 | -RemoveApplicationPartitions:$true ` 12 | -IgnoreLastDCInDomainMismatch:$true ` 13 | -Force:$true 14 | 15 | -------------------------------------------------------------------------------- /AD-DNS/Export-ADOUs.ps1: -------------------------------------------------------------------------------- 1 | # http://c-nergy.be/blog/?p=4709 2 | Get-ADOrganizationalUnit -filter { name -like "*" } | select Name, DistinguishedName | Export-Csv AD_OU_Tree.csv -NoTypeInformation -------------------------------------------------------------------------------- /AD-DNS/Get-FsmoRoles.ps1: -------------------------------------------------------------------------------- 1 | # List FSMO roles 2 | $Roles = Get-ADDomainController -Filter * | Select-Object Name, Domain, Forest, OperationMasterRoles | Where-Object { $_.OperationMasterRoles } | Format-Table -AutoSize -------------------------------------------------------------------------------- /AD-DNS/Move-FsmoRoles.ps1: -------------------------------------------------------------------------------- 1 | # Moves all FSMO roles to the specified server 2 | Move-ADDirectoryServerOperationMasterRole -Identity "dc2" –OperationMasterRole PDCEmulator, RIDMaster, InfrastructureMaster, SchemaMaster, DomainNamingMaster -------------------------------------------------------------------------------- /AD-DNS/Out-DnsZone.ps1: -------------------------------------------------------------------------------- 1 | # Get zones from local DNS server and export them to a text file. Export-DnsServerZone chokes on file paths, file will write to \Windows\SYSTEM32\dns 2 | Get-DnsServerZone | Where-Object { $_.IsAutoCreated -eq $False } | foreach { Export-DnsServerZone -Name $_.ZoneName -FileName ($_.ZoneName + ".txt") -Verbose } -------------------------------------------------------------------------------- /AD-DNS/Promote-DomainController.ps1: -------------------------------------------------------------------------------- 1 | # Install Active Directory role 2 | Install-windowsfeature AD-domain-services 3 | 4 | 5 | # Install DC into existing domain 6 | Import-Module ADDSDeployment 7 | Install-ADDSDomainController ` 8 | -NoGlobalCatalog:$false ` 9 | -Credential (Get-Credential) ` 10 | -CriticalReplicationOnly:$false ` 11 | -DatabasePath "C:\windows\NTDS" ` 12 | -DomainName "home.stealthpuppy.com" ` 13 | -InstallDns:$false ` 14 | -LogPath "C:\windows\NTDS" ` 15 | -NoRebootOnCompletion:$false ` 16 | -ReplicationSourceDC "HV1.home.stealthpuppy.com" ` 17 | -SiteName "Home" ` 18 | -SysvolPath "C:\windows\SYSVOL" ` 19 | -Force:$true 20 | 21 | 22 | # Install DC into existing domain 23 | Import-Module ADDSDeployment 24 | Install-ADDSDomainController ` 25 | -NoGlobalCatalog:$false ` 26 | -CreateDnsDelegation:$false ` 27 | -Credential (Get-Credential) ` 28 | -CriticalReplicationOnly:$false ` 29 | -DatabasePath "C:\windows\NTDS" ` 30 | -DomainName "home.stealthpuppy.com" ` 31 | -InstallDns:$true ` 32 | -LogPath "C:\windows\NTDS" ` 33 | -NoRebootOnCompletion:$false ` 34 | -SiteName "Home" ` 35 | -SysvolPath "C:\windows\SYSVOL" ` 36 | -Force:$true 37 | 38 | # Install first DC into new forest 39 | $Password = "Passw0rd" 40 | $SPassword = ConvertTo-SecureString -String $Password -AsPlainText -Force 41 | 42 | Install-ADDSForest -CreateDnsDelegation:$false -DatabasePath "C:\Windows\NTDS" -DomainMode "Win2012R2" -DomainName "home.stealthpuppy.com" -DomainNetbiosName "home" -ForestMode "Win2012R2" -InstallDns:$false -LogPath "C:\Windows\NTDS" -NoRebootOnCompletion:$true -SysvolPath "C:\Windows\SYSVOL" -SafeModeAdministratorPassword $SPassword -Force:$true -------------------------------------------------------------------------------- /AD-DNS/README.md: -------------------------------------------------------------------------------- 1 | # AD-DNS 2 | 3 | Scripts for managing Active Directory and DNS. Use at your own risk. 4 | -------------------------------------------------------------------------------- /AD-DNS/Restore-DnsZone.ps1: -------------------------------------------------------------------------------- 1 | # Authenticate to the remote host (CredSSP already configured for workgroup machines) 2 | $Username = "Administrator" 3 | $Password = "Passw0rd" 4 | $SPassword = ConvertTo-SecureString -String $Password -AsPlainText -Force 5 | $cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Username, $SPassword 6 | # $cred = Get-Credential -UserName Administrator -Message "Please enter the password." 7 | $nuc1 = New-CimSession -Credential $cred -ComputerName nuc1 8 | 9 | Add-DnsServerPrimaryZone -CimSession $nuc1 -ZoneFile home.stealthpuppy.com.dns -Name home.stealthpuppy.com -LoadExisting 10 | Add-DnsServerPrimaryZone -CimSession $nuc1 -ZoneFile _msdcs.home.stealthpuppy.com.dns -Name _msdcs.home.stealthpuppy.com -LoadExisting 11 | Add-DnsServerPrimaryZone -CimSession $nuc1 -ZoneFile 0.168.192.in-addr.arpa.dns -Name 0.168.192.in-addr.arpa -LoadExisting 12 | -------------------------------------------------------------------------------- /HyperV/Delete-VM.ps1: -------------------------------------------------------------------------------- 1 | Function Delete-VM { 2 | <# 3 | .SYNOPSIS 4 | Removes a virtual machine on a Hyper-V host and deletes the associated virtual hard disks. 5 | 6 | .DESCRIPTION 7 | Removes a virtual machine on a Hyper-V host and deletes the associated virtual hard disks. 8 | 9 | .PARAMETER ComputerName 10 | The hostname of the Hyper-V host where the virtual machine exists. 11 | Defaults to the local host if not specified. 12 | 13 | .PARAMETER VM 14 | The virtual machine to delete. Multiple virtual machines can be specified. 15 | 16 | .PARAMETER Username 17 | The username used to connect to a remove Hyper-V server. 18 | 19 | .PARAMETER Password 20 | The password for the username specified. 21 | 22 | .PARAMETER CimSession 23 | A CIM session to a remote host that can be passed to this function for authenticating against a remote host. 24 | 25 | .EXAMPLE 26 | PS C:\> Delete-VM -ComputerName hyperv1 -VM sql1, web1 27 | 28 | Rmoves the virtual machines sql1 and web1 from the host hyperv1 and deletes their associated virtual hard disks. 29 | 30 | .NOTES 31 | NAME: Delete-VM.ps1 32 | VERSION: 1.0 33 | AUTHOR: Aaron Parker 34 | LASTEDIT: April 16, 2016 35 | 36 | .LINK 37 | http://stealthpuppy.com 38 | #> 39 | [CmdletBinding(SupportsShouldProcess = $False, ConfirmImpact = "High", DefaultParameterSetName = "Auth")] 40 | PARAM ( 41 | [Parameter(Mandatory = $True, HelpMessage = "Specify a virtual machine to delete.")] 42 | [string[]]$VM, 43 | 44 | [Parameter(ParameterSetName = "Auth", Mandatory = $False, HelpMessage = "Specify a host where the target virtual machine exists.")] 45 | [string]$ComputerName = $env:COMPUTERNAME, 46 | 47 | [Parameter(ParameterSetName = "Auth", Mandatory = $False, HelpMessage = "Specify a username used to connect to a remote Hyper-V host.")] 48 | [string]$Username, 49 | 50 | [Parameter(ParameterSetName = "Auth", Mandatory = $False, HelpMessage = "Specify a password for authentication with the specified username.")] 51 | [string]$Password, 52 | 53 | [Parameter(ParameterSetName = "Cim", Mandatory = $False)] 54 | [Microsoft.Management.Infrastructure.CimSession]$CimSession 55 | ) 56 | 57 | BEGIN { 58 | 59 | # If username/password passed to function, create a CIM session to authenticate to remote host 60 | If ($PSBoundParameters['Username']) { 61 | If ($PSBoundParameters['Password']) { 62 | 63 | # Convert a string to a secure string and create a credential object 64 | $SecurePassword = ConvertTo-SecureString -String $Password -AsPlainText -Force 65 | $cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Username, $SecurePassword 66 | 67 | # Try a connection to the remote host before creating the CIM session 68 | Try { 69 | Test-Connection -ComputerName $ComputerName -Count 1 -ErrorVariable TestError -ErrorAction Stop 70 | } 71 | Catch { 72 | Write-Error "Failed to connect with error: " $TestError 73 | Return 74 | } 75 | 76 | Try { 77 | $cim = New-CimSession -Credential $cred -ComputerName $ComputerName -ErrorVariable CimError -ErrorAction Stop 78 | } 79 | Catch { 80 | Write-Error "Failed to to create CIM session with: " $CimError 81 | Return 82 | } 83 | 84 | } 85 | } 86 | 87 | # If a CIM session passed just use that. (no need to pass one CIM session into another, though) 88 | If ($PSBoundParameters['CimSession']) { 89 | $cim = $CimSession 90 | } 91 | } 92 | 93 | PROCESS { 94 | 95 | # Walk through each VM and remove it along with its virtual hard disks 96 | # Need to fix Invoke-Command 97 | ForEach ( $v in $VM ) { 98 | $machine = Get-VM -CimSession $cim -Name $v -ErrorVariable Error -ErrorAction SilentlyContinue 99 | $VHDs = $machine | Get-VMHardDiskDrive 100 | Invoke-Command -ComputerName $ComputerName -Credential $cred -ScriptBlock { param($VHDs) ForEach ( $vhd in $VHDs) { Remove-Item -Path $vhd.Path -Force -Confirm:$False -Verbose } } -Args $VHDs 101 | $machine | Remove-VM -Force -Verbose 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /HyperV/Get-HvMem.ps1: -------------------------------------------------------------------------------- 1 | Function Get-HvMem { 2 | <# 3 | .SYNOPSIS 4 | Return Hyper-V host RAM details. 5 | 6 | .DESCRIPTION 7 | This function returns the total available RAM, RAM in use by VMs and the available RAM on a Hyper-V host. 8 | 9 | .PARAMETER ComputerName 10 | Specifies one or more Hyper-V hosts to retrieve stats from. 11 | 12 | .EXAMPLE 13 | Get-HvRAM -ComputerName hyperv1 14 | 15 | .NOTES 16 | 17 | .LINK 18 | http://stealthpuppy.com/hyperv-memory-powershell 19 | 20 | #> 21 | param( 22 | [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Hyper-V host.")] 23 | [string[]]$ComputerName = $(throw = "Please specify a remote Hyper-V host to gather memory details from.") 24 | ) 25 | 26 | # Create an array to return 27 | $allStats = @() 28 | 29 | ForEach ( $computer in $ComputerName ) { 30 | 31 | # Create an array to contain this computer's metrics 32 | $a = @() 33 | 34 | # Get details for Hyper-V host 35 | $vmHost = Get-VMHost -ComputerName $computer 36 | 37 | If ($vmHost) { 38 | 39 | # Get total RAM consumed by running VMs. 40 | $total = 0 41 | Get-VM -ComputerName hv1 | Where-Object { $_.State -eq "Running" } | Select-Object Name, MemoryAssigned | ForEach-Object { $total = $total + $_.MemoryAssigned } 42 | 43 | #Get available RAM via performance counters 44 | $Bytes = Get-Counter -ComputerName $computer -Counter "\Memory\Available Bytes" 45 | 46 | # Convert values to GB 47 | $availGB = ($Bytes[0].CounterSamples.CookedValue / 1GB) 48 | $hostGB = ($vmhost.MemoryCapacity / 1GB) 49 | $vmInUse = ($total / 1GB) 50 | 51 | # Construct an array of properties to return 52 | $item = New-Object PSObject 53 | 54 | # Add host name 55 | $item | Add-Member -type NoteProperty -Name 'Name' -Value $vmHost.Name 56 | 57 | # Host RAM in GB 58 | $item | Add-Member -type NoteProperty -Name 'HostRAMGB' -Value $hostGB 59 | 60 | # In use RAM in GB 61 | $item | Add-Member -type NoteProperty -Name 'VMInUseGB' -Value $vmInUse 62 | 63 | # System used in GB 64 | $item | Add-Member -type NoteProperty -Name 'SystemUsedGB' -Value ($hostGB - ($vmInUse + $availGB)) 65 | 66 | # Available RAM in GB 67 | $item | Add-Member -type NoteProperty -Name 'AvailableGB' -Value $availGB 68 | $a += $item 69 | } 70 | 71 | # Add the current machine details to the array to return 72 | $allStats += $a 73 | } 74 | Return $allStats 75 | } 76 | -------------------------------------------------------------------------------- /HyperV/Get-HyperVUUID.ps1: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------- 2 | # Author: Aaron Parker 3 | # Desc: Function that uses retrieves the UUID from a specified VM and 4 | # formats it into the right format for use with MDT/SCCM etc 5 | # Date: Aug 18, 2014 6 | # Site: http://stealthpuppy.com 7 | #--------------------------------------------------------------------------- 8 | 9 | 10 | Function Get-HypervVMUUID { 11 | 12 | <# 13 | .SYNOPSIS 14 | Retrieve the UUID from a virtual machine or set of virtual machines. 15 | 16 | .DESCRIPTION 17 | This function will retrieve the UUID from from a virtual machine or set of virtual machines from a Hyper-V host. 18 | 19 | .PARAMETER ComputerName 20 | Specifies the host from which to query the virtual machine or set of virtual machines. 21 | 22 | .PARAMETER VM 23 | Specifies the virtual machine or set of virtual machines (a comma delimited list) from which to obtain the UUID/s. 24 | 25 | .EXAMPLE 26 | PS C:\> Get-HypervVMUUID -ComputerName hv1 -VM win71, win72 27 | 28 | This command retrieves the UUIDs from the virtual machines win71 and win72 from the host hv1. 29 | 30 | .EXAMPLE 31 | PS C:\> Get-HypervVMUUID -VM win71, win72 32 | 33 | This command retrieves the UUIDs from the virtual machines win71 and win72 from the local host. 34 | 35 | .EXAMPLE 36 | PS C:\> Get-HypervVMUUID 37 | 38 | This command retrieves the UUIDs from the all of the virtual machines on the local host. 39 | 40 | .NOTES 41 | http://stealthpuppy.com/retrieving-a-vms-uuid-from-hyperv/ for support information. 42 | 43 | .LINK 44 | 45 | http://stealthpuppy.com/retrieving-a-vms-uuid-from-hyperv/ 46 | 47 | #> 48 | 49 | [cmdletbinding(SupportsShouldProcess = $True)] 50 | param( 51 | [Parameter(Mandatory = $false, HelpMessage = "Specifies one or more Hyper-V hosts from which virtual machine UUIDs are to be retrieved. NetBIOS names, IP addresses, and fully-qualified domain names are allowable. The default is the local computer — use ""localhost"" or a dot (""."") to specify the local computer explicitly.")] 52 | [string]$ComputerName, 53 | 54 | [Parameter(Mandatory = $false, Position = 0, HelpMessage = "Specifies the virtual machine from which to retrieve the UUID.")] 55 | [string[]]$VM 56 | ) 57 | 58 | # If ComputerName parameter is not specified, set value to the local host 59 | If (!$ComputerName) { $ComputerName = "." } 60 | 61 | # If VM parameter is specified, return those VMs, else return all VMs 62 | If ($VM) { 63 | $UUIDs = Get-VM -ComputerName $ComputerName -VM $VM -ErrorAction SilentlyContinue | Select-Object Name, @{Name = "BIOSGUID"; Expression = { (Get-WmiObject -ComputerName $_.ComputerName -Namespace "root\virtualization\v2" -Class Msvm_VirtualSystemSettingData -Property BIOSGUID -Filter ("InstanceID = 'Microsoft:{0}'" -f $_.VMId.Guid)).BIOSGUID } } 64 | } 65 | Else { 66 | $UUIDs = Get-VM -ComputerName $ComputerName -ErrorAction SilentlyContinue | Select-Object Name, @{Name = "BIOSGUID"; Expression = { (Get-WmiObject -ComputerName $_.ComputerName -Namespace "root\virtualization\v2" -Class Msvm_VirtualSystemSettingData -Property BIOSGUID -Filter ("InstanceID = 'Microsoft:{0}'" -f $_.VMId.Guid)).BIOSGUID } } 67 | } 68 | 69 | # Remove curly brackets from the UUIDs and return the array 70 | ForEach ( $UID in $UUIDs ) { $UID.BIOSGUID = $UID.BIOSGUID -replace "}"; $UID.BIOSGUID = $UID.BIOSGUID -replace "{" } 71 | Return $UUIDs 72 | } -------------------------------------------------------------------------------- /HyperV/Get-HyperVVMIPs.ps1: -------------------------------------------------------------------------------- 1 | Get-VM -ComputerName hv1 | ? { $_.State -eq "Running" } | Get-VMNetworkAdapter | select VMName, IPAddresses -------------------------------------------------------------------------------- /HyperV/Get-Memory-Notes.ps1: -------------------------------------------------------------------------------- 1 | test-wsman -ComputerName hv1.home.stealthpuppy.com -Authentication Default 2 | 3 | Invoke-Command -ComputerName hv1.home.stealthpuppy.com { Measure-Command { gwmi Win32_PerfRawData_PerfOS_Memory } | fl sec*, mill* } 4 | 5 | New-Item -Path $pshome\Modules\BNTools\BNTools.psm1 -Type file -Force -OutVariable bnmod 6 | notepad $bnmod 7 | 8 | 9 | $servers = 'dc1', 'sql1', 'xd71', 'appv1' 10 | $cred = Get-Credential 'home\administrator' 11 | $servers | % { Copy-Item -Recurse -Force -Verbose -Path $pshome\Modules\BNTools -Destination \\$_\c$\Windows\System32\WindowsPowerShell\v1.0\Modules } 12 | 13 | $cred = Get-Credential 'hv1\administrator' 14 | Invoke-Command -Credential $cred -ComputerName $servers -ScriptBlock { if ((Get-ExecutionPolicy) -ne 'RemoteSigned') { Set-ExecutionPolicy RemoteSigned -Force } } 15 | Invoke-Command -Credential $cred -ComputerName $servers -ScriptBlock { Get-ExecutionPolicy } | ft pscomp*, value -auto 16 | 17 | Invoke-Command -Credential $cred -ComputerName $servers -ScriptBlock { ipmo bntools; Get-Memory -Detailed -Format } 18 | 19 | 20 | Invoke-Command -Credential $cred -ComputerName $servers -HideComputerName -ScriptBlock { ipmo bntools; Get-Memory -Format } | select * -excl run* | sort 'Use%' -Descending | Out-GridView -Title 'Server Memory Stats' 21 | 22 | 23 | 24 | Set-Item WSMAN:\localhost\client\trustedhosts -Value dc1 -concatenate -Force -------------------------------------------------------------------------------- /HyperV/GetVMUptime.ps1.txt: -------------------------------------------------------------------------------- 1 | # GetVMUptime.ps1 2 | 3 | Get-VM | Where {$_.state -eq 'running'} | Sort Uptime | Select Name,Uptime,@{N="Memory/MB";E={$_.MemoryAssigned/1MB}} 4 | -------------------------------------------------------------------------------- /HyperV/HyperV-ControlledMDTDeployment.ps1: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------- 2 | # Controlled MDT deployment 3 | #--------------------------------------------------------------------------- 4 | 5 | # Set variables (change these to arguments later) 6 | $MDTBootISO = "D:\TechDays\Boot\Test_LiteTouchPE_x86.iso" 7 | # $MDTBootISO = "D:\Deployment\Boot\KelwayLiteTouchPE_x86.iso" 8 | $TargetVM_OSName = "WIN81" 9 | $TaskSequenceID = "W8_ENT_X64" 10 | $MachineObjectOU = "OU=Desktops,DC=lab,DC=com" 11 | $TargetVM_Name = "WIN81" 12 | $VMTemplate = "VMTemplate" 13 | $DeploymentShare = "D:\TechDays" 14 | $CustomSettingsINI = "$DeploymentShare\Control\CustomSettings.ini" 15 | $VMHostName = "HYPERV1" 16 | $SwitchName = "Internal" 17 | $Seconds = 5 18 | 19 | # Get Hyper-V host properties 20 | Write-Verbose "Getting host properties." -ForegroundColor Green 21 | $VMHost = Get-VMHost -ComputerName $VMHostName 22 | 23 | # Remove any existing VM 24 | [string]$vDiskPath = "path" 25 | $TargetVM = Get-VM | Where-Object { $_.Name -eq $TargetVM_Name } 26 | If ( $TargetVM -ne $Null ) { 27 | Write-Verbose "Found existing VM." -ForegroundColor Green 28 | $VMHardDisk = $TargetVM | Get-VMHardDiskDrive 29 | $vDiskPath = $VMHardDisk.Path 30 | If ( $TargetVM.State -eq "Running" ) { Write-Verbose "Stopping VM..." -ForegroundColor Green ; Stop-VM -VM $TargetVM -Force -Confirm:$False } 31 | Do { 32 | $TargetVM = Get-VM | Where-Object { $_.name -eq $TargetVM_Name } 33 | Switch ($TargetVM.State) { 34 | { $_ -eq "Off" } { $Seconds = 0; break } 35 | { $_ -eq "Running" } { $Seconds = 10; break } 36 | } 37 | Start-Sleep $Seconds 38 | } Until ( $TargetVM.State -eq "Off" ) 39 | Write-Verbose "Removing VM." -ForegroundColor Green 40 | Remove-VM -VM $TargetVM -Force -Confirm:$False -Verbose 41 | If (Test-Path $vDiskPath) { Remove-Item $vDiskPath -Force -Confirm:$False -Verbose } 42 | } 43 | 44 | # Create a VM 45 | Write-Verbose "Creating new VM $TargetVM_Name." -ForegroundColor Green 46 | $VHDPath = $VMHost.VirtualHardDiskPath 47 | New-VM –Name $TargetVM_Name -SwitchName $SwitchName –MemoryStartupBytes 1024MB -NewVHDSizeBytes 64GB -NewVHDPath "$VHDPath$TargetVM_Name.vhdx" -BootDevice CD -Verbose 48 | Get-VMDvdDrive -VMName $TargetVM_Name | Set-VMDvdDrive -Path $MDTBootISO -Verbose 49 | Set-VM -Name $TargetVM_Name -ProcessorCount 2 -AutomaticStartAction Nothing -AutomaticStopAction Shutdown -DynamicMemory -Verbose 50 | 51 | # Get the target VM's UUID 52 | Write-Verbose "Retrieving target VM's UUID." -ForegroundColor Green 53 | $UUIDs = Get-VM -ComputerName $VMHostName | Select-Object Name, VMId, @{Name = "BIOSGUID"; Expression = { (Get-WmiObject -ComputerName $_.ComputerName -Namespace "root\virtualization" -Class Msvm_VirtualSystemSettingData -Property BIOSGUID -Filter ("InstanceID = 'Microsoft:{0}'" -f $_.VMId.Guid)).BIOSGUID } } 54 | $UUID = $UUIDs | Where-Object { $_.Name -eq $TargetVM_Name } | select BIOSGUID 55 | $TargetVMUUID = $UUID.BIOSGUID -Replace "{", "" 56 | $TargetVMUUID = $TargetVMUUID -Replace "}", "" 57 | 58 | 59 | # Connect to the MDT share 60 | Write-Verbose "Connecting to MDT." -ForegroundColor Green 61 | Add-PSSnapin 'Microsoft.BDD.PSSNAPIN' -ErrorAction SilentlyContinue 62 | # If ((Get-Module 'Microsoft.BDD.PSSNAPIN') -eq $null) { throw "Module did not load" } 63 | If (!(Test-Path MDT:)) { New-PSDrive -Name MDT -Root $DeploymentShare -PSProvider MDTPROVIDER } 64 | 65 | # Write settings for the target VM to MDT CustomSettings.ini 66 | # open INI file, create or edit section, assign task sequence, configure deployment wizard 67 | Write-Verbose "Backing up CustomSettings.ini." -ForegroundColor Green 68 | If (!(Test-Path "$DeploymentShare\Control\CustomSettings-Backup.ini")) { Copy-Item $CustomSettingsINI "$DeploymentShare\Control\CustomSettings-Backup.ini" -Force } 69 | 70 | # Create new content for the INI file and write back to the file 71 | Write-Verbose "Adding control section for $TargetVM_Name." -ForegroundColor Green 72 | $Category1 = @{"OSDComputerName" = $TargetVM_OSName; "TaskSequenceID" = $TaskSequenceID; "MachineObjectOU" = $MachineObjectOU; "WindowsUpdate" = "FALSE"; "SkipSummary" = "YES"; "SkipTaskSequence" = "YES"; "SkipApplications" = "YES"; "SkipLocaleSelection" = "YES"; "SkipDomainMembership" = "YES"; "SkipTimeZone" = "YES"; "SkipComputerName" = "YES"; "SkipUserData" = "YES"; "SkipComputerBackup" = "YES" } 73 | $NewINIContent = @{$TargetVMUUID = $Category1 } 74 | Write-Verbose "Writing to CustomSettings.ini." -ForegroundColor Green 75 | Out-IniFile -InputObject $NewINIContent -FilePath $CustomSettingsINI -Force ASCII -Append 76 | 77 | # Clean up the MDT monitor data for the target VM if it exists 78 | Write-Verbose "Clearing MDT monitor data." -ForegroundColor Green 79 | Get-MDTMonitorData -Path MDT: | Where-Object { $_.Name -eq $TargetVM_OSName } | Remove-MDTMonitorData -Path MDT: 80 | 81 | # Start the VM 82 | Write-Verbose "Starting $TargetVM_Name..." -ForegroundColor Green 83 | $TargetVM = Get-VM | Where-Object { $_.Name -eq $TargetVM_Name } 84 | If (!($TargetVM.State -eq "On")) { $TargetVM | Start-VM -Verbose } 85 | 86 | # Wait for the OS deployment to start before monitoring 87 | # This may require user intervention to boot the VM from the MDT ISO if an OS exists on the vDisk 88 | If ((Test-Path variable:InProgress) -eq $True) { Remove-Variable -Name InProgress } 89 | Do { 90 | $InProgress = Get-MDTMonitorData -Path MDT: | Where-Object { $_.Name -eq $TargetVM_OSName -and $_.DeploymentStatus -eq 1 } 91 | If ($InProgress) { 92 | If ($InProgress.PercentComplete -eq 100) { 93 | $Seconds = 30 94 | $TSStarted = $False 95 | Write-Verbose "Waiting for task sequence to begin..." -ForegroundColor Green 96 | } 97 | Else { 98 | $Seconds = 0 99 | $TSStarted = $True 100 | Write-Verbose "Task sequence has begun. Moving to monitoring phase." -ForegroundColor Green 101 | } 102 | } 103 | Else { 104 | $Seconds = 30 105 | $TSStarted = $False 106 | Write-Verbose "Waiting for task sequence to begin..." -ForegroundColor Green 107 | } 108 | Start-Sleep -Seconds $Seconds 109 | } Until ($TSStarted -eq $True) 110 | 111 | # Connect to VM console 112 | Write-Verbose "Opening console to $TargetVM_Name." -ForegroundColor Green 113 | vmconnect $VMHostName $TargetVM.VMName 114 | 115 | # Monitor the MDT OS deployment once started 116 | Write-Verbose "Monitoring task sequence." -ForegroundColor Green 117 | Do { 118 | $InProgress = Get-MDTMonitorData -Path MDT: | Where-Object { $_.Name -eq $TargetVM_OSName } 119 | If ( $InProgress.PercentComplete -lt 100 ) { 120 | If ( $InProgress.StepName.Length -eq 0 ) { $StatusText = "Waiting for update" } Else { $StatusText = $InProgress.StepName } 121 | Write-Progress -Activity "Task sequence in progress" -Status $StatusText -PercentComplete $InProgress.PercentComplete 122 | Switch ($InProgress.PercentComplete) { 123 | { $_ -lt 25 } { $Seconds = 35; break } 124 | { $_ -lt 50 } { $Seconds = 30; break } 125 | { $_ -lt 75 } { $Seconds = 10; break } 126 | { $_ -lt 100 } { $Seconds = 0; break } 127 | } 128 | Start-Sleep -Seconds $Seconds 129 | } 130 | } Until ($InProgress.CurrentStep -eq $InProgress.TotalSteps) 131 | Write-Verbose "Task sequence complete." -ForegroundColor Green 132 | Start-Sleep -Seconds 8 133 | 134 | # Shutdown the target VM 135 | # Write-Verbose "Shutting down $TargetVM_Name." -ForegroundColor Green 136 | # $TargetVM = Get-VM | Where-Object { $_.Name -eq $TargetVM_Name } 137 | # If ( $TargetVM -ne $Null ) { 138 | # If ( $TargetVM.State -eq "Running" ) { Stop-VM -VM $TargetVM -Force } 139 | # Do { 140 | # $TargetVM = Get-VM | Where-Object { $_.name -eq $TargetVM_Name } 141 | # Switch ($TargetVM.State) { 142 | # {$_ -eq "Off"}{$Seconds = 0; break} 143 | # {$_ -eq "Running"}{$Seconds = 10; break} 144 | # } 145 | # Start-Sleep $Seconds 146 | # } Until ( $TargetVM.State -eq "Off" ) 147 | # } 148 | 149 | Write-Verbose "Script complete." -ForegroundColor Green -------------------------------------------------------------------------------- /HyperV/New-LabVM.ps1: -------------------------------------------------------------------------------- 1 | Function New-LabVM { 2 | <# 3 | .SYNOPSIS 4 | Creates a new VM configured for the lab environment. 5 | 6 | .DESCRIPTION 7 | Creates a new VM specifically configured for my home lab environment. 8 | Create v1 or v2 VMs and configure VM for specific paths on the host machine. 9 | 10 | .PARAMETER Name 11 | Specify the name of the virtual machine. 12 | 13 | .PARAMETER CPUs 14 | Specify the number of vCPUs to configure the machine with. Will default to 2 vCPUs. 15 | No more than 2 vCPUs will be accepted to ensure the host is not overcommited. 16 | 17 | .PARAMETER VHDSize 18 | Specify the size of the VHDX in GB assigned to the VM. Will default to 64 GB. 19 | Sizes between 32 and 128 GB will be accepted. 20 | 21 | .PARAMETER Host 22 | Specify the target host. Will default to NUC1. 23 | 24 | .PARAMETER Generation 25 | Specify the version of the VM to use. Will default to generation 2 VMs. 26 | Values of 1 and 2 are accepted. 27 | 28 | .EXAMPLE 29 | PS C:\> New-LabVM -Name APPV1 -CPUs 2 -VHDSize 64 30 | 31 | .NOTES 32 | NAME: New-LabVM.ps1 33 | VERSION: 1.0 34 | AUTHOR: Aaron Parker 35 | LASTEDIT: April 06, 2016 36 | 37 | .LINK 38 | http://stealthpuppy.com 39 | #> 40 | [CmdletBinding (SupportsShouldProcess = $False, ConfirmImpact = "Low", DefaultParameterSetName = "")] 41 | PARAM ( 42 | [Parameter (Mandatory = $True, Position = 0, ValueFromPipeline = $True, HelpMessage = "Specify a name for the virtual machine.")] 43 | [alias("VMName")] 44 | [string]$Name, 45 | 46 | [Parameter (Mandatory = $False, HelpMessage = "Specify the host where the new virtual machine will be created.")] 47 | [alias("ComputerName")] 48 | [string]$VmHost = "nuc1.home.stealthpuppy.com", 49 | 50 | [Parameter (Mandatory = $False, HelpMessage = "Specify the number of vCPUs to be assigned to the virtual machine.")] 51 | [alias("ProcessorCount")] 52 | [ValidateSet(1, 2)][int]$CPUs = 2, 53 | 54 | [Parameter (Mandatory = $False, HelpMessage = "Specify the size of the virtual hard disk assigned to the virtual machine.")] 55 | [ValidateRange(32, 128)][int]$VHDSize = 64, 56 | 57 | [Parameter (Mandatory = $False, HelpMessage = "Specify the virtual machine generation.")] 58 | [alias("Version")] 59 | [ValidateSet(1, 2)]$Generation = 2 60 | ) 61 | 62 | BEGIN { 63 | Try { 64 | 65 | # See if a CIM session to the specified host exists 66 | $CimSession = Get-CimSession | Where-Object { $_.ComputerName -eq $VMHost } 67 | If ( $CimSession ) { 68 | 69 | # If a CIM session to the host exists, use that for authentication and connect to host 70 | $oVmHost = Get-VMHost -CimSession $CimSession -ErrorAction "Stop" 71 | } 72 | Else { 73 | 74 | # If no CIM session exists, assume pass-through authentication will work and connect to host 75 | $oVmHost = Get-VMHost -ComputerName $VMHost -ErrorAction "Stop" 76 | } 77 | } 78 | 79 | Catch { 80 | Write-Error "Error acessing host $VMHost $($_.Exception.Message)" 81 | } 82 | 83 | # $VmNetwork = $oVmHost | Select-Object -ExpandProperty ExternalNetworkAdapters 84 | $memoryStartupBytes = 768MB 85 | $newVHDSizeBytes = 64GB 86 | $isoPath = "C:\ISOs\LiteTouchPE_x64.iso" 87 | $bootDevice = "CD" 88 | $SnapshotFileLocation = "D:\Hyper-V\Snapshots" 89 | $SmartPagingFilePath = "C:\Hyper-V" 90 | } 91 | 92 | PROCESS { 93 | 94 | $Params = @{ 95 | Name = $Name 96 | MemoryStartupBytes = $memoryStartupBytes 97 | # NewVHDSizeBytes = $newVHDSizeBytes 98 | # NewVHDPath = $oVmHost.VirtualHardDiskPath + "\$Name.vhdx" 99 | SwitchName = $oVmHost.ExternalNetworkAdapters[0].SwitchName 100 | Generation = $Generation 101 | BootDevice = $bootDevice 102 | } 103 | 104 | # Create the new virtual machine 105 | If ( $CimSession ) { 106 | Write-Host "CIM Session" 107 | $VHD = New-VHD -Path ($oVmHost.VirtualHardDiskPath + "\$Name.vhdx") -SizeBytes $newVHDSizeBytes -Dynamic -CimSession $CimSession -Verbose 108 | $VM = New-VM @Params -VHDPath ($oVmHost.VirtualHardDiskPath + "\$Name.vhdx") -CimSession $CimSession -Verbose 109 | } 110 | Else { 111 | Write-Host "ComputerName" 112 | $VHD = New-VHD -Path ($oVmHost.VirtualHardDiskPath + "\$Name.vhdx") -SizeBytes $newVHDSizeBytes -Dynamic -ComputerName $VmHost -Verbose 113 | $VM = New-VM @Params -VHDPath ($oVmHost.VirtualHardDiskPath + "\$Name.vhdx") -ComputerName $VmHost -Verbose 114 | } 115 | 116 | # Set additional VM properties 117 | $VM | Set-VM -ProcessorCount $CPUs -AutomaticStartAction Nothing -AutomaticStopAction Shutdown -DynamicMemory -Verbose 118 | $VM | Set-VM -SnapshotFileLocation $SnapshotFileLocation -SmartPagingFilePath $SmartPagingFilePath 119 | $VM | Get-VMDvdDrive | Set-VMDvdDrive -Path $isoPath 120 | $DVD = ($VM | Get-VMDvdDrive) 121 | 122 | # Set VM boot order based on the VM generation 123 | Switch ( $Generation ) { 124 | 125 | # Generation 1 VM 126 | 1 { 127 | $VM | Set-VMBios -StartupOrder @("CD", "IDE", "LegacyNetworkAdapter", "Floppy") 128 | } 129 | 130 | # Generation 2 VM 131 | 2 { 132 | $VM | Set-VMFirmware -FirstBootDevice $DVD -EnableSecureBoot On 133 | } 134 | 135 | Default { Write-Host "Opps, shouldn't have gotten here." } 136 | } 137 | } 138 | 139 | END { 140 | 141 | # Get updated VM object and return it 142 | If ( $CimSession ) { 143 | $VM = Get-VM -Name $Name -CimSession $CimSession 144 | } 145 | Else { 146 | $VM = Get-VM -Name $Name -ComputerName $Host 147 | } 148 | Return $VM 149 | } 150 | } -------------------------------------------------------------------------------- /HyperV/README.md: -------------------------------------------------------------------------------- 1 | # HyperV 2 | 3 | Hyper-V management scripts. Script for managing Hyper-V deployments, creating virtual machines etc. Use at your own risk. 4 | -------------------------------------------------------------------------------- /HyperV/Set-Power.ps1: -------------------------------------------------------------------------------- 1 | # Import-Module ActiveDirectory 2 | 3 | $cred = Get-Credential Iammred\administrator 4 | $cn = Get-ADComputer -Filter "OperatingSystem -like '* 2012 *'" 5 | $cim = New-CimSession -ComputerName $cn.name -Credential $cred 6 | 7 | 8 | $cred = Get-Credential home\administrator 9 | $cim = New-CimSession -ComputerName hv1 -Credential $cred 10 | Get-CimInstance -Name root\cimv2\power -Class win32_PowerPlan -Filter "IsActive = 'True'" -CimSession $cim | Format-Table PsComputerName, ElementName 11 | 12 | # ------------------- 13 | 14 | # SetServerPowerSaverPlan.ps1 15 | Import-Module ActiveDirectory 16 | 17 | $cred = Get-Credential Iammred\administrator 18 | $cn = Get-ADComputer -Filter "OperatingSystem -like '* 2012 *'" 19 | $cim = New-CimSession -ComputerName $cn.name -Credential $cred 20 | 21 | $cred = Get-Credential home\administrator 22 | $cim = New-CimSession -ComputerName hv1 -Credential $cred 23 | $p = Get-CimInstance -Name root\cimv2\power -Class win32_PowerPlan -Filter "ElementName = 'High performance'" -CimSession $cim 24 | Invoke-CimMethod -InputObject $p[0] -MethodName Activate -CimSession $cim 25 | 26 | 27 | $p = Get-CimInstance -Name root\cimv2\power -Class win32_PowerPlan -Filter "ElementName = 'Power Saver'" -CimSession $cim 28 | Invoke-CimMethod -InputObject $p[0] -MethodName Activate -CimSession $cim 29 | 30 | 31 | Get-WmiObject -ComputerName hv1 -Class Win32_PowerPlan -Namespace root\cimv2\power -------------------------------------------------------------------------------- /HyperV/Snippets-HyperV.ps1: -------------------------------------------------------------------------------- 1 | # Get a VM 2 | $hvHost = "hv1.home.stealthpuppy.com" 3 | Get-VM -ComputerName $hvHost 4 | 5 | # Remove a virtual hard disk 6 | $VHD = Get-VMHardDiskDrive -ComputerName $hvHost -VMName xd71 7 | Get-VM -ComputerName $hvHost -Name xd71 | Remove-VM -Force 8 | Invoke-Command -ComputerName $hvHost { Remove-Item "C:\Users\Public\Documents\Hyper-V\Virtual Hard Disks\XD71.vhdx" } 9 | 10 | 11 | # Create a VM 12 | $vmName = "APPV1" 13 | $bootISO = "D:\MDT\Automata\Boot\LiteTouchPE_x64.iso" 14 | $switchName = "External Ethernet" 15 | $vhdPath = (Get-VMHost -ComputerName $hvHost).VirtualHardDiskPath 16 | New-VM -ComputerName $hvHost -Generation 2 –Name $vmName -SwitchName $switchName –MemoryStartupBytes 768MB -NewVHDSizeBytes 64GB -NewVHDPath "$VHDPath\$vmName.vhdx" -Verbose 17 | $dvdDrive = Get-VMDvdDrive -ComputerName $hvHost -VMName $vmName 18 | $vhdDrive = Get-VHD -ComputerName $hvHost -VMName $vmName 19 | Set-VMDvdDrive -ComputerName $hvHost -VMName $vmName -Path $bootISO -Verbose 20 | Set-VMFirmware -ComputerName $hvHost -VMName $vmName -FirstBootDevice $dvdDrive -Verbose 21 | Set-VM -ComputerName $hvHost -Name $vmName -ProcessorCount 2 -AutomaticStartAction Nothing -AutomaticStopAction Shutdown -DynamicMemory -Verbose 22 | Get-VMScsiController -ComputerName $hvHost -VMName $vmName | Add-VMDvdDrive -Path $bootISO 23 | Get-VM -ComputerName $hvHost -VMName $vmName | Add-VMDvdDrive -Path $bootISO 24 | 25 | # Set all VM DVD drives to nothing 26 | Get-VM | Get-VmDvdDrive | foreach { Set-VMDvdDrive -Path $Null } -------------------------------------------------------------------------------- /HyperV/Start-SequentialVMs.ps1: -------------------------------------------------------------------------------- 1 | Function Start-SequentialVMs { 2 | <# 3 | .SYNOPSIS 4 | Starts a list of VMs. 5 | 6 | .DESCRIPTION 7 | This function starts a list of VMs sequentially. It will wait until a VM is booted, optionally pause for a number of seconds, before starting the next VM. 8 | 9 | .PARAMETER ComputerName 10 | Specifies the Hyper-V host to start the VM on. 11 | 12 | .PARAMETER VM 13 | Specifies a list of VMs to start. 14 | 15 | .PARAMETER Wait 16 | Specifies a number of seconds to wait after the previous VM has booted successfully. Defaults to 180 seconds. 17 | 18 | .PARAMETER ShowProgress 19 | Specified whether to show progress as VMs are started. 20 | 21 | .EXAMPLE 22 | Start-SequentialVMs -ComputerName hyperv1 -VMList "sql1", "pvs1", "xd71" -Wait 20 23 | 24 | .NOTES 25 | 26 | .LINK 27 | http://stealthpuppy.com/sequential-start-vms 28 | 29 | #> 30 | param( 31 | [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Hyper-V host.")] 32 | [string]$ComputerName = $(throw = "Please specify a remote Hyper-V host to start VMs on."), 33 | 34 | [Parameter(Mandatory = $true, Position = 1, HelpMessage = "List of VMs to start.")] 35 | [string[]]$VMList = $(throw = "Please specifiy a list of VMs to start"), 36 | 37 | [Parameter(Mandatory = $false)] 38 | [int]$Wait = 180, 39 | 40 | [Parameter(Mandatory = $false)] 41 | [bool]$ShowProgress 42 | ) 43 | 44 | # Connect to Hyper-V host before attempting to start VMs. Stop script if unable to connect 45 | Write-Verbose "Connecting to VM host." 46 | Get-VMHost -ComputerName $ComputerName -Verbose $False -ErrorAction Stop 47 | 48 | # Start progress at 0 49 | $Percent = 0 50 | 51 | # Step through list of provided VMs 52 | ForEach ( $vm in $VMList ) { 53 | 54 | # Convert current location in list of VMs to a percentage 55 | $Percent = ($VMList.IndexOf($vm) / $VMList.Count) * 100 56 | 57 | # Show progress if specified on the command line 58 | If ($ShowProgress -eq $True) { Write-Progress -Activity "Starting VMs." -Status "Starting VM $vm." -PercentComplete $Percent } 59 | 60 | # Get status for current VM 61 | Remove-Variable currentVM -ErrorAction SilentlyContinue 62 | Write-Verbose "Getting status for VM $vm..." 63 | $currentVM = Get-VM -ComputerName $ComputerName -Name $vm -ErrorAction SilentlyContinue 64 | 65 | # If the VM exists, then power it on if it is in an Off state 66 | If ($currentVM.Length -gt 0) { 67 | If ($currentVM.State -eq "Off" ) { 68 | Start-VM -ComputerName $ComputerName -Name $vm -Verbose 69 | 70 | # Wait for VM to boot and report a heartbeat 71 | Write-Verbose "Waiting for VM heartbeat." 72 | Do { 73 | Start-Sleep -Milliseconds 100 74 | } Until ((Get-VMIntegrationService $currentVM | ? { $_.name -eq "Heartbeat" }).PrimaryStatusDescription -eq "OK") 75 | 76 | # Wait the specified number of seconds before booting the next VM, unless this is the last VM in the list 77 | If ($Wait -gt 0 -and $VMList.IndexOf($vm) -lt ($VMList.Count - 1)) { 78 | Write-Verbose "Waiting for $Wait seconds before starting next VM." 79 | Start-Sleep -Seconds $Wait 80 | } 81 | 82 | } 83 | Else { 84 | Write-Verbose "VM $vm already running." 85 | } 86 | 87 | } 88 | Else { 89 | Write-Error -Message "Unable to find VM $vm on host $ComputerName." -Category ObjectNotFound 90 | } 91 | 92 | } 93 | 94 | Write-Verbose "Started VMs." 95 | 96 | # Show progress if specified on the command line 97 | If ($ShowProgress -eq $True) { Write-Progress -Activity "Starting VMs." -Status "Started all VMs." -PercentComplete 100 } 98 | Start-Sleep -Seconds 1 99 | } -------------------------------------------------------------------------------- /HyperV/Test-New-LabVM.ps1: -------------------------------------------------------------------------------- 1 | # Having issues with New-LABVM.ps1 function, this script for testing / troubleshooting 2 | 3 | Function New-LabVM { 4 | 5 | $VmHost = Get-VMHost -ComputerName "nuc1.home.stealthpuppy.com" -ErrorAction "Stop" 6 | $VmNetwork = $VmHost | Select-Object -ExpandProperty ExternalNetworkAdapters 7 | $memoryStartupBytes = 768MB 8 | $newVHDSizeBytes = 64GB 9 | $isoPath = "C:\ISOs\LiteTouchPE_x64.iso" 10 | $bootDevice = "CD" 11 | $SnapshotFileLocation = "D:\Hyper-V\Snapshots" 12 | $SmartPagingFilePath = "C:\Hyper-V" 13 | $Name = "TEST1" 14 | $Generation = 2 15 | 16 | $Params = @{ 17 | Name = $Name 18 | MemoryStartupBytes = $memoryStartupBytes 19 | # NewVHDSizeBytes = $newVHDSizeBytes 20 | # NewVHDPath = $VmHost.VirtualHardDiskPath + "\$Name.vhdx" 21 | SwitchName = $VmHost.ExternalNetworkAdapters[0].SwitchName 22 | Generation = $Generation 23 | BootDevice = $bootDevice 24 | } 25 | 26 | # Create the new virtual machine 27 | $VHD = New-VHD -Path "D:\Hyper-V\Virtual Hard Disks\TEST1.vhdx" -SizeBytes $newVHDSizeBytes -Dynamic -ComputerName $VmHost.Name -Verbose 28 | $VM = New-VM -Name $Name -MemoryStartupBytes 768MB -SwitchName "VM External Network" -Generation 2 -BootDevice "CD" -VHDPath "D:\Hyper-V\Virtual Hard Disks\TEST1.vhdx" -ComputerName $VmHost.Name -Verbose 29 | 30 | # Set additional VM properties 31 | $VM | Set-VM -ProcessorCount $CPUs -AutomaticStartAction Nothing -AutomaticStopAction Shutdown -DynamicMemory -Verbose 32 | $VM | Set-VM -SnapshotFileLocation $SnapshotFileLocation -SmartPagingFilePath $SmartPagingFilePath 33 | $VM | Get-VMDvdDrive | Set-VMDvdDrive -Path $isoPath 34 | $DVD = ($VM | Get-VMDvdDrive) 35 | 36 | } -------------------------------------------------------------------------------- /HyperV/Working-Set-HypervDefaults.ps1: -------------------------------------------------------------------------------- 1 | # Authenticate to the remote host (CredSSP already configured for workgroup machines) 2 | $Username = "Administrator" 3 | $Password = "password" 4 | $SPassword = ConvertTo-SecureString -String $Password -AsPlainText -Force 5 | $cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Username, $SPassword 6 | # $cred = Get-Credential -UserName Administrator -Message "Please enter the password." 7 | $nuc1 = New-CimSession -Credential $cred -ComputerName nuc1.home.stealthpuppy.com 8 | 9 | # Get configuration from remote host 10 | $VmHost = Get-VMHost -CimSession $nuc1 | select * | Format-List 11 | $VmHost.VirtualHardDiskPath 12 | $VmHost.VirtualMachinePath 13 | 14 | # Get configuration from a VM 15 | $VM = Get-VM -CimSession $nuc1 -Name DC1 | select Path, ConfigurationLocation, CheckpointFileLocation, SmartPagingFilePath, SnapshotFileLocation 16 | $VM.CheckpointFileLocation 17 | $VM.ConfigurationLocation 18 | $VM.SmartPagingFilePath 19 | $VM.SnapshotFileLocation 20 | $VM.Path 21 | 22 | # View path properties on all VMs 23 | Get-Vm -CimSession $nuc1 | select Name, Path, ConfigurationLocation, CheckpointFileLocation, SmartPagingFilePath, SnapshotFileLocation 24 | 25 | 26 | # Set default folders 27 | Machines 28 | Virtual Hard Disks 29 | Checkpoints 30 | Smart Paging File 31 | 32 | # Set all DVD drives on a VM to 'None' 33 | Get-VMDvdDrive -CimSession $nuc1 -VMName DC1 | foreach { Set-VMDvdDrive -VMDvdDrive $_ -Path $Null } 34 | 35 | # Move all VMs where XML configuration is stored in specific location to new location, on a remote Hyper-V host. 36 | Get-VM -CimSession $nuc1 | Where-Object { $_.ConfigurationLocation -eq "D:\Hyper-V\Machines" } | Move-VMStorage -VirtualMachinePath D:\Hyper-V -Verbose 37 | 38 | # Set Checkpoints and Smart Paging locations on all VMs except for DC1 on a remote Hyper-V host. 39 | Get-VM -CimSession $nuc1 | Where-Object { $_.Name -ne "DC1" } | Set-VM -SnapshotFileLocation "D:\Hyper-V" -SmartPagingFilePath "C:\Hyper-V" -Verbose -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Aaron Parker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Network/Set-WindowsFirewall.ps1: -------------------------------------------------------------------------------- 1 | # http://windowsitpro.com/windows-server-2012/controlling-windows-firewall-powershell 2 | 3 | # Allow everything inbound and outbound without disabling Windows Firewall 4 | Set-NetFirewallProfile -all -DefaultInboundAction Allow -DefaultOutboundAction Allow -------------------------------------------------------------------------------- /Network/Snippets-Networking.ps1: -------------------------------------------------------------------------------- 1 | # Get physical network adapters from Intel 2 | $NetAdapter = Get-NetAdapter | Where-Object { $_.ifDesc -Like "Intel*" } | select * 3 | 4 | # Get IPv4 IP Address of interface with adapter set to DHCP 5 | $InfIPv4 = Get-NetIPAddress | Where-Object { $_.InterfaceAlias -eq (Get-NetIPInterface | Where-Object { $_.AddressFamily -eq "IPv4" -and $_.Dhcp -eq "Enabled" } | select *).InterfaceAlias } | Where-Object { $_.AddressFamily -eq "IPv4" } 6 | 7 | Set-NetIPAddress -InterfaceAlias $InfIPv4.InterfaceAlias -IPAddress 192.168.0.6 -AddressFamily IPv4 -PrefixLength 24 -Confirm:$False -Verbose 8 | New-NetIPAddress -InterfaceAlias $InfIPv4.InterfaceAlias -IPAddress 192.168.0.6 -DefaultGateway 192.168.0.1 -AddressFamily IPv4 -PrefixLength 24 -Confirm:$False -Verbose 9 | 10 | Set-DnsClientServerAddress -InterfaceAlias $InfIPv4.InterfaceAlias -ServerAddresses 127.0.0.1 -Confirm:$False -Verbose 11 | 12 | 13 | -------------------------------------------------------------------------------- /Network/ipconfig.ps1: -------------------------------------------------------------------------------- 1 | # https://blogs.technet.microsoft.com/josebda/2015/04/18/windows-powershell-equivalents-for-common-networking-commands-ipconfig-ping-nslookup/ 2 | 3 | Get-NetIPConfiguration 4 | Get-NetIPAddress | Sort InterfaceIndex | ft InterfaceIndex, InterfaceAlias, AddressFamily, IPAddress, PrefixLength -AutoSize 5 | Get-NetIPAddress | ? AddressFamily -EQ IPv4 | ft –AutoSize 6 | Get-NetAdapter Wi-Fi | Get-NetIPAddress | ft -AutoSize -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Infrastructure 2 | 3 | Various scripts and tools for managing infrastructure roles 4 | -------------------------------------------------------------------------------- /StorageSpaces/Config-StorageSpaces.ps1: -------------------------------------------------------------------------------- 1 | # Variables 2 | $StoragePoolName = "StoragePool" 3 | $TieredSpaceName = "TieredSpace" 4 | $ResiliencySetting = "Simple" 5 | $SSDTierName = "SSDTier" 6 | $HDDTierName = "HDDTier" 7 | 8 | # Drives to be included in the storage pool 9 | $Drives = @(3, 4) 10 | 11 | # Reset drives 12 | ForEach ($Drive in $Drives) { 13 | Get-PhysicalDisk -DeviceNumber $Drive | Reset-PhysicalDisk 14 | } 15 | 16 | # Ensure enough poolable drives exist after reset 17 | If ((Get-PhysicalDisk -CanPool $True).Count -lt 2) { Write-Error -Message "Failed to find enough poolable drives." } 18 | 19 | # Set unspecified drives to HDD 20 | Get-PhysicalDisk -CanPool $True | Where-Object { $_.MediaType -eq "Unspecified" } | ` 21 | Set-PhysicalDisk -MediaType HDD 22 | 23 | # Store all physical disks that can be pooled into a variable, $PoolableDisks 24 | # $PoolableDisks = (Get-PhysicalDisk -CanPool $True | Where-Object { $_.MediaType -ne "Unspecified" }) 25 | $PoolableDisks = Get-PhysicalDisk | Where-Object { ($_.DeviceId -eq 3) -or ($_.DeviceId -eq 4) } 26 | 27 | # Create a new Storage Pool using the disks in variable $PoolableDisks 28 | $SubSysName = (Get-StorageSubSystem).FriendlyName 29 | New-StoragePool -PhysicalDisks $PoolableDisks -StorageSubSystemFriendlyName $SubSysName -FriendlyName $StoragePoolName ` 30 | -AutoWriteCacheSize $True -ResiliencySettingNameDefault $ResiliencySetting 31 | 32 | # View the disks in the Storage Pool just created 33 | Get-StoragePool -FriendlyName $StoragePoolName | Get-PhysicalDisk | ` 34 | Select-Object -Property FriendlyName, MediaType | Format-List 35 | 36 | # Upgrade the storage pool 37 | Get-StoragePool -IsPrimordial $False | Update-StoragePool -Confirm:$False 38 | 39 | # Create two tiers in the Storage Pool created. One for SSD disks and one for HDD disks 40 | $SSDTier = New-StorageTier -StoragePoolFriendlyName $StoragePoolName -FriendlyName $SSDTierName -MediaType SSD ` 41 | -ResiliencySettingName $ResiliencySetting 42 | $HDDTier = New-StorageTier -StoragePoolFriendlyName $StoragePoolName -FriendlyName $HDDTierName -MediaType HDD ` 43 | -ResiliencySettingName $ResiliencySetting 44 | 45 | # Identify tier sizes within this storage pool 46 | $SSDTierSize = ((Get-StorageTierSupportedSize -FriendlyName $SSDTierName).TierSizeMax) / 1GB 47 | $HDDTierSize = ((Get-StorageTierSupportedSize -FriendlyName $HDDTierName).TierSizeMax) / 1GB 48 | 49 | # Create a new virtual disk in the pool with a name of TieredSpace using the SSD and HDD tiers 50 | New-VirtualDisk -StoragePoolFriendlyName $StoragePoolName -FriendlyName $TieredSpaceName -StorageTiers $SSDTier, $HDDTier ` 51 | -StorageTierSizes 230GB, 930GB -ResiliencySettingName $ResiliencySetting 52 | 53 | 54 | # --- 55 | Get-StoragePool -IsPrimordial $False | Remove-StoragePool -Confirm:$False 56 | -------------------------------------------------------------------------------- /Tests/PesterOutput.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/Infrastructure/872a530dbccdf335c8adcee391f245d983260aa1/Tests/PesterOutput.PNG -------------------------------------------------------------------------------- /Tests/README.md: -------------------------------------------------------------------------------- 1 | # Operating System Validation 2 | 3 | [Pester](https://github.com/pester/Pester/wiki) tests used to validate a Windows Server or Windows 10 operating system install. Tests can be used to validate a specific Windows configuration to ensure it's ready for deployment. Tests could be run manually or at the end of an install (e.g. via MDT) to ensure the Windows image meets the expected configuration. 4 | 5 | ## Running Tests 6 | 7 | Tests can be run with the `Invoke-Pester` command: 8 | 9 | ```powershell 10 | Invoke-Pester -Script .\Windows101903.Tests.ps1 -OutputFormat NUnitXml -OutputFile .\TestResults.xml 11 | ``` 12 | 13 | The output should look similar to the screenshot below: 14 | 15 | ![Pester output](https://raw.githubusercontent.com/aaronparker/Infrastructure/master/Tests/PesterOutput.PNG) 16 | -------------------------------------------------------------------------------- /Tests/RunTests.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | AppVeyor tests script. 4 | #> 5 | #Requires -RunAsAdministrator 6 | #Requires -PSEdition Desktop 7 | [CmdletBinding()] 8 | Param () 9 | 10 | # Setup test environment 11 | Write-Host -ForegroundColor Cyan "`n`tChecking required module versions." 12 | $Modules = @("Pester", "LatestUpdate", "VcRedist") 13 | ForEach ($Module in $Modules) { 14 | If ([Version]((Find-Module -Name $Module).Version) -gt [Version]((Get-Module -Name $Module | Select-Object -Last 1).Version)) { 15 | Write-Host -ForegroundColor Cyan "`tInstalling latest $Module module." 16 | Install-Module -Name $Module -SkipPublisherCheck -Force 17 | } 18 | Import-Module -Name $Module -Force 19 | } 20 | 21 | # Invoke Pester tests 22 | $res = Invoke-Pester -Path (Resolve-Path -Path $PWD) -OutputFormat NUnitXml -OutputFile (Join-Path -Path (Resolve-Path -Path $PWD) -ChildPath "TestsResults.xml") -PassThru 23 | If ($res.FailedCount -gt 0) { Throw "$($res.FailedCount) tests failed." } 24 | -------------------------------------------------------------------------------- /Tests/WindowsImage.Tests.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Runs Pester tests against a Windows 10 VM to confirm a desired configuration 4 | #> 5 | #Requires -RunAsAdministrator 6 | #Requires -PSEdition Desktop 7 | [CmdletBinding()] 8 | Param() 9 | 10 | # Environment setup 11 | Write-Host -ForegroundColor Cyan "`n`tGetting operating system version." 12 | $Builds = @{ 13 | "18362" = "1903" 14 | "17763" = "1809" 15 | "17134" = "1803" 16 | "16299" = "1709" 17 | "15063" = "1703" 18 | "14393" = "1607" 19 | } 20 | $Version = $Builds.$((([System.Environment]::OSVersion.Version).Build).ToString()) 21 | Write-Host -ForegroundColor Cyan "`tGetting Windows edition." 22 | $Edition = Get-WindowsEdition -Online 23 | Write-Host -ForegroundColor Cyan "`tGetting installed Hotfixes." 24 | $InstalledUpdates = Get-Hotfix 25 | Write-Host -ForegroundColor Cyan "`tGetting Windows feature states." 26 | Switch -Regex ($Edition.Edition) { 27 | "Pro|Enterprise" { 28 | Write-Host -ForegroundColor Cyan "`tPlatform is Windows 10." 29 | $Features = Get-WindowsOptionalFeature -Online 30 | $NotInstalled = @("SMB1Protocol", "SMB1Protocol-Client", "SMB1Protocol-Server", "Printing-XPSServices-Features", ` 31 | "WindowsMediaPlayer", "Internet-Explorer-Optional-amd64", "WorkFolders-Client", "FaxServicesClientPackage", "TelnetClient") 32 | $Installed = @("NetFx4-AdvSrvs", "NetFx3") 33 | $VcReleases = @("2010", "2012", "2013", "2019") 34 | } 35 | "ServerStandard|ServerDatacenter" { 36 | Write-Host -ForegroundColor Cyan "`tPlatform is Windows Server." 37 | $Features = Get-WindowsFeature 38 | $NotInstalled = @("FS-SMB1", "XPS-Viewer") 39 | $Installed = @("Windows-Defender", "NET-Framework-45-Core", "NET-Framework-45-Features", ` 40 | "NET-Framework-Core", "NET-Framework-Features") 41 | $VcReleases = @("2019") 42 | } 43 | } 44 | Switch ([intptr]::Size) { 45 | 4 { $Proc = "x86" } 46 | 8 { $Proc = "x64" } 47 | } 48 | 49 | Describe "Windows version validation tests" { 50 | Context "Validate operating system" { 51 | It "Should be running a valid Windows edition" { 52 | $EditionMatch = $Edition.Edition -match "Enterprise|ServerStandard|ServerDatacenter" 53 | $EditionMatch | Should -BeTrue 54 | } 55 | } 56 | } 57 | 58 | Describe "Windows feature validation tests" { 59 | Context "Validate removed or disabled features" { 60 | ForEach ($Feature in $NotInstalled) { 61 | It "Should not have $Feature installed" { 62 | Switch -Regex ($Edition.Edition) { 63 | "Pro|Enterprise" { 64 | ($Features | Where-Object { $_.FeatureName -eq $Feature }).State | Should -Be "Disabled" 65 | } 66 | "ServerStandard|ServerDatacenter" { 67 | ($Features | Where-Object { $_.Name -eq $Feature }).Installed | Should -Be $False 68 | } 69 | } 70 | } 71 | } 72 | } 73 | Context "Validate installed features" { 74 | ForEach ($Feature in $Installed) { 75 | It "Should have $Feature installed" { 76 | Switch -Regex ($Edition.Edition) { 77 | "Pro|Enterprise" { 78 | ($Features | Where-Object { $_.FeatureName -eq $Feature }).State | Should -Be "Enabled" 79 | } 80 | "ServerStandard|ServerDatacenter" { 81 | ($Features | Where-Object { $_.Name -eq $Feature }).Installed | Should -Be $True 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | 89 | Describe -Tag 'Updates' "Windows update validation tests" { 90 | Context "Validate Cumulative updates" { 91 | It "Should have the latest Cumulative Update installed" { 92 | Write-Host -ForegroundColor Cyan "`n`tGetting latest Cumulative Update." 93 | $LatestUpdate = Get-LatestCumulativeUpdate -OperatingSystem Windows10 -Version $Version | Where-Object { $_.Architecture -eq $Proc } | Select-Object -Last 1 94 | $PreviousUpdate = Get-LatestCumulativeUpdate -OperatingSystem Windows10 -Version $Version -Previous | Where-Object { $_.Architecture -eq $Proc } | Select-Object -Last 1 95 | If (($InstalledUpdates.HotFixID -match $LatestUpdate.KB) -or ($InstalledUpdates.HotFixID -match $PreviousUpdate.KB)) { $KBMatch = $True } 96 | $KBMatch | Should -BeTrue 97 | } 98 | } 99 | 100 | Context "Validate Servicing Stack updates" { 101 | It "Should have the latest Servicing Stack Update installed" { 102 | Write-Host -ForegroundColor Cyan "`n`tGetting latest Servicing Stack Update." 103 | $LatestUpdate = Get-LatestServicingStackUpdate -OperatingSystem Windows10 -Version $Version | Where-Object { $_.Architecture -eq $Proc } | Select-Object -Last 1 104 | $PreviousUpdate = Get-LatestServicingStackUpdate -OperatingSystem Windows10 -Version $Version -Previous | Where-Object { $_.Architecture -eq $Proc } | Select-Object -Last 1 105 | If (($InstalledUpdates.HotFixID -match $LatestUpdate.KB) -or ($InstalledUpdates.HotFixID -match $PreviousUpdate.KB)) { $KBMatch = $True } 106 | $KBMatch | Should -BeTrue 107 | } 108 | } 109 | 110 | Context "Validate .NET Framework updates" { 111 | Write-Host -ForegroundColor Cyan "`n`tGetting latest .NET Framework Update." 112 | $Updates = Get-LatestNetFrameworkUpdate -OperatingSystem Windows10 | Where-Object { ($_.Architecture -eq $Proc) -and ($_.Version -eq "$Version") } 113 | ForEach ($Update in $Updates) { 114 | It "Should have the latest .NET Framework Update installed" { 115 | $Update.KB | Should -BeIn $InstalledUpdates.HotFixID 116 | } 117 | } 118 | } 119 | 120 | Context "Validate Adobe Flash updates" { 121 | It "Should have the latest Adobe Flash Player Update installed" { 122 | Write-Host -ForegroundColor Cyan "`n`tGetting latest Adobe Flash Player Update." 123 | $LatestUpdate = Get-LatestAdobeFlashUpdate -OperatingSystem Windows10 -Version $Version | Where-Object { $_.Architecture -eq $Proc } | Select-Object -Last 1 124 | $PreviousUpdate = Get-LatestAdobeFlashUpdate -OperatingSystem Windows10 -Version $Version -Previous | Where-Object { $_.Architecture -eq $Proc } | Select-Object -Last 1 125 | If (($InstalledUpdates.HotFixID -match $LatestUpdate.KB) -or ($InstalledUpdates.HotFixID -match $PreviousUpdate.KB)) { $KBMatch = $True } 126 | $KBMatch | Should -BeTrue 127 | } 128 | } 129 | } 130 | 131 | Describe "Windows regional settings validation tests" { 132 | Context "Validate language and regional settings" { 133 | It "Should have Regional settings set to en-AU" { 134 | (Get-Culture).Name | Should -Be "en-AU" 135 | } 136 | It "Should have the correct Windows display language" { 137 | (Get-UICulture).Name | Should -BeIn @("en-US", "en-AU", "en-GB") 138 | } 139 | It "Should have System locale set to en-AU" { 140 | (Get-WinSystemLocale).Name | Should -Be "en-AU" 141 | } 142 | } 143 | 144 | Context "Validate time zone settings" { 145 | It "Should be set to the correct time zone" { 146 | (Get-TimeZone).Id | Should -Be "AUS Eastern Standard Time" 147 | } 148 | It "Should have daylight savings supported" { 149 | (Get-TimeZone).SupportsDaylightSavingTime | Should -Be $True 150 | } 151 | } 152 | } 153 | 154 | Describe "Windows software validation tests" { 155 | Context "Validate installed Visual C++ Redistributables" { 156 | Write-Host -ForegroundColor Cyan "`n`tGetting installed Visual C++ Redistributables." 157 | $InstalledVcRedists = Get-InstalledVcRedist 158 | ForEach ($VcRedist in (Get-VcList -Release $VcReleases)) { 159 | It "Should have Visual C++ Redistributable $($VcRedist.Release) $($VcRedist.Architecture) $($VcRedist.Version) installed" { 160 | $Match = $InstalledVcRedists | Where-Object { ($_.Release -eq $VcRedist.Release) -and ($_.Architecture -eq $VcRedist.Architecture) } 161 | $Match.ProductCode | Should -Match $VcRedist.ProductCode 162 | } 163 | } 164 | } 165 | } 166 | 167 | Write-Host "" 168 | -------------------------------------------------------------------------------- /Various/Get-AvailableModule.ps1: -------------------------------------------------------------------------------- 1 | Function Get-AvailableModule { 2 | Param ([string]$Name) 3 | If ( -Not (Get-Module -Name $Name)) { 4 | If (Get-Module -ListAvailable | Where-Object { $_.Name -eq $Name }) { 5 | Import-Module -Name $Name 6 | Return $True 7 | # If module available then import 8 | } 9 | Else { 10 | Return $False 11 | # Module not available 12 | } 13 | } 14 | Else { 15 | Return $True 16 | # Module already loaded 17 | } 18 | } #End Function -------------------------------------------------------------------------------- /Various/Get-CommandArguments.ps1: -------------------------------------------------------------------------------- 1 | $a = Get-Command New-BrokerDesktopGroup 2 | $a.ParameterSets[0] | select -ExpandProperty parameters | ft name, ismandatory, aliases 3 | 4 | -------------------------------------------------------------------------------- /Various/Remove-ItemsByAge.ps1: -------------------------------------------------------------------------------- 1 | function remove-itembyage { 2 | <# 3 | .SYNOPSIS 4 | remove items from folders recursively. 5 | 6 | .DESCRIPTION 7 | this function removes items older than a specified age from the target folder 8 | 9 | .PARAMETER Days 10 | Specifies the amount of days since the file was last written to you wish to filter on. 11 | 12 | .PARAMETER Path 13 | Specifies the path to the folder you wish to search recursively. 14 | 15 | .PARAMETER Silent 16 | Instructs the function not to return any output. 17 | 18 | .EXAMPLE 19 | PS C:\> remove-itembyage -days 0 -path $recent 20 | 21 | This command searches the $recent directory, for any files, then deletes them. 22 | 23 | .EXAMPLE 24 | PS C:\> remove-itembyage -days 5 -path $recent 25 | 26 | This command searches the $recent directory, for files older than 5 days, then deletes them. 27 | 28 | .EXAMPLE 29 | PS C:\> remove-itembyage -days 10 -path $appdata -typefilter "txt,log" 30 | 31 | This command searches the $cookies directory, for files older than 10 days and end with txt or log extensions, then deletes them. 32 | 33 | .EXAMPLE 34 | PS C:\> remove-itembyage -days 10 -path $cookies -typefilter "txt,log" -silent 35 | 36 | This command searches the $cookies directory, for files older than 10 days and end with txt or log extensions, then deletes them without a report. 37 | 38 | .NOTES 39 | http://blog.stealthpuppy.com/user-virtualization/profile-clean-up-script-powershell-edition/ for support information. 40 | 41 | .LINK 42 | 43 | http://blog.stealthpuppy.com/user-virtualization/profile-clean-up-script-powershell-edition/ 44 | 45 | #> 46 | 47 | [cmdletbinding(SupportsShouldProcess = $True)] 48 | param( 49 | [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Number of days to filter by, E.G. ""14""")] 50 | [int]$days, 51 | [Parameter(Mandatory = $true, Position = 1, HelpMessage = "Path to files you wish to delete")] 52 | [string]$path, 53 | [string]$typefilter, 54 | [switch]$silent) 55 | 56 | #check for silent switch 57 | if ($silent) { $ea = "Silentlycontinue" } 58 | Else { $ea = "Continue" } 59 | 60 | #check for typefilter, creates an array if specified. 61 | if (!($typefilter)) { $filter = "*" } 62 | Else { $filter = foreach ($item in $typefilter.split(",")) { $item.insert(0, "*.") } } 63 | 64 | if (Test-Path $path) { 65 | $now = Get-Date 66 | $datefilter = $now.adddays(-$days) 67 | foreach ($file in Get-ChildItem "$path\*" -Recurse -Force -Include $filter | where { $_.PSIsContainer -eq $false -and $_.lastwritetime -le $datefilter -and $_.name -ne "desktop.ini" }) { 68 | if (!($silent)) { Write-Host "Deleting: $($file.fullname)" } 69 | Remove-Item -LiteralPath $file.fullname -Force -ea $ea 70 | }#end for 71 | }#end if 72 | 73 | Else { 74 | if (!($silent)) { Write-Warning "the path specified does not exist! ($path)" } 75 | }#end else 76 | }#end function 77 | 78 | #Get KnownFolder Paths 79 | $appdata = $env:appdata 80 | $Cookies = (New-Object -com shell.application).namespace(289).Self.Path 81 | $History = (New-Object -com shell.application).namespace(34).Self.Path 82 | $recent = (New-Object -com shell.application).namespace(8).Self.Path 83 | $profile = $env:userprofile 84 | 85 | #commands 86 | remove-itembyage -days 0 -path $appdata -typefilter "txt,log" -silent 87 | remove-itembyage -days 90 -path $cookies -silent 88 | remove-itembyage -days 14 -path $recent -silent 89 | remove-itembyage -days 21 -path $history -silent 90 | remove-itembyage -days 14 -path "$appdata\Microsoft\office\Recent" -silent 91 | -------------------------------------------------------------------------------- /Various/Snippets.ps1: -------------------------------------------------------------------------------- 1 | # Find used space on the C: drive 2 | $Drive = Get-WmiObject Win32_LogicalDisk | Where-Object { $_.DeviceID -eq $env:SystemDrive } 3 | $UsedSpace = ($Drive.Size - $Drive.FreeSpace)/1GB 4 | Write-Host $UsedSpace 5 | 6 | # Run all EXE files in subfolders one-by-one 7 | Get-ChildItem -Recurse -File -Include *.exe | Select FullName | ForEach { Start-Process -FilePath $_.FullName -Wait } 8 | 9 | # Enable .NET Framework 3.5 on Windows 8/10 10 | Enable-WindowsOptionalFeature -Online -FeatureName NetFx3 -Source "\\mcfly\Deployment\Reference\Operating Systems\Windows 10 Enterprise x64\sources\sxs" 11 | -------------------------------------------------------------------------------- /vSphere/BootVMs.ps1: -------------------------------------------------------------------------------- 1 | $VIServer = "10.130.36.222" 2 | Add-PSSnapin 'vmware.VimAutomation.core' 3 | Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope Session -Confirm:$False 4 | $MyCredentials = Get-Credential 5 | Connect-VIServer -Server $VIServer -Credential $MyCredentials -Verbose 6 | 7 | If (!($TargetVM.PowerState -eq "PoweredOn")) { $TargetVM | Start-VM -Verbose } 8 | 9 | $VMs = Get-VM | Where-Object { $_.Name -like "ILIO-EX*" } 10 | ForEach ( $VM in $VMs ) { 11 | If ( $VM.PowerState -eq "PoweredOff" ) { $VM | Start-VM -Verbose -RunAsync } 12 | } 13 | -------------------------------------------------------------------------------- /vSphere/Delete-vSphereVM.ps1: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------- 2 | # Author: Aaron Parker 3 | # Desc: Remove a VM from vSphere 4 | # Date: Mar 24, 2013 5 | # Site: http://blog.stealthpuppy.com 6 | #--------------------------------------------------------------------------- 7 | 8 | Function Delete-vSphereVM { 9 | <# 10 | .SYNOPSIS 11 | Removes a virtual machine completely 12 | 13 | .DESCRIPTION 14 | Removes a virtula machine completely 15 | 16 | .PARAMETER Name 17 | Specifies the VM to delete. 18 | 19 | .EXAMPLE 20 | PS C:\> Delete-vSphereVM -Name "W7VM1" 21 | 22 | Deletes the virtual machine named W7VM1. 23 | 24 | .EXAMPLE 25 | PS C:\> $VM | Delete-vSphereVM 26 | 27 | Deletes the virtual machine piped to this function. 28 | 29 | .NOTES 30 | See http://blog.stealthpuppy.com/ for support information. 31 | 32 | .LINK 33 | 34 | 35 | #> 36 | 37 | [CmdletBinding(SupportsShouldProcess = $True)] 38 | Param( 39 | [Parameter(Mandatory = $False, ValueFromPipeline = $True, HelpMessage = "Specify the VM to retrive the UUID from.")] 40 | [System.Object]$VM, 41 | 42 | [Parameter(Mandatory = $False, ValueFromPipeline = $False, HelpMessage = "Specify the VM name to delete.")] 43 | [System.Object]$Name 44 | ) 45 | 46 | BEGIN { 47 | } 48 | 49 | PROCESS { 50 | 51 | # Remove any existing VM 52 | # $targetVM = Get-VM | Where-Object { $_.Name -eq $templateVM_Name } 53 | If ( $VM.PowerState -eq "PoweredOn" ) { Stop-VM -VM $VM -Confirm:$False } 54 | Do { 55 | $targetVM = Get-VM | Where-Object { $_.name -eq $vm.Name } 56 | Switch ($TargetVM.PowerState) { 57 | { $_ -eq "PoweredOff" } { $Seconds = 0; break } 58 | { $_ -eq "PoweredOn" } { $Seconds = 10; break } 59 | } 60 | Start-Sleep $Seconds 61 | } Until ( $targetVM.PowerState -eq "PoweredOff" ) 62 | Remove-VM -VM $targetVM -DeletePermanently -Confirm:$False 63 | } 64 | 65 | END { 66 | # Return the UUID 67 | # Return $UUIDfixed 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /vSphere/Get-vSphereVMUUID.ps1: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------- 2 | # Author: Aaron Parker 3 | # Desc: Function that uses retrieves the UUID from a specified VM and 4 | # transposes it into the right format for use with MDT/SCCM etc 5 | # Date: Mar 24, 2013 6 | # Site: http://blog.stealthpuppy.com 7 | # 8 | # Original code snippets from: 9 | # http://communities.vmware.com/thread/239735 10 | # http://www.keithsmithonline.com/2013/02/powershell-show-vmware-vm-UUID.html 11 | #--------------------------------------------------------------------------- 12 | 13 | Function Get-vSphereVMUUID { 14 | <# 15 | .SYNOPSIS 16 | Retrieves the UUID from a specified VM and formats it correctly for use with MDT/SCCM etc. 17 | 18 | .DESCRIPTION 19 | Retrieves the UUID from a specified VM and formats it correctly for use with MDT/SCCM etc. Returns the UUID as a string that can be passed to other functions. 20 | 21 | Requires that a VM object is passed to the function. That object will first have to be created before being passed to this function. 22 | 23 | .PARAMETER VM 24 | Specifies the VM to retrieve the UUID from. 25 | 26 | .EXAMPLE 27 | PS C:\> Get-vSphereVMUUID -VM "W7VM1" 28 | 29 | Retrieves the UUID from a VM named W7VM1. 30 | 31 | .EXAMPLE 32 | PS C:\> $VM | Get-vSphereVMUUID 33 | 34 | Retrieves the UUID from a VM piped to this function. 35 | 36 | .NOTES 37 | See http://blog.stealthpuppy.com/ for support information. 38 | 39 | .LINK 40 | http://blog.stealthpuppy.com/code/retrieving-a-vms-uuid-from-vsphere/ 41 | 42 | #> 43 | 44 | [CmdletBinding(SupportsShouldProcess = $True)] 45 | Param( 46 | [Parameter(Mandatory = $True, ValueFromPipeline = $True, HelpMessage = "Specify the VM to retrive the UUID from.")] 47 | [System.Object]$VM 48 | ) 49 | 50 | BEGIN { 51 | } 52 | 53 | PROCESS { 54 | # Retrive UUID from vSphere 55 | $UUID = $VM | % { (Get-View $_.Id).config.UUID } 56 | 57 | #Transpose UUID into expected format 58 | # Section 1 59 | $UUID11 = $UUID.Substring(0, 2) 60 | $UUID12 = $UUID.Substring(2, 2) 61 | $UUID13 = $UUID.Substring(4, 2) 62 | $UUID14 = $UUID.Substring(6, 2) 63 | 64 | # Section 2 65 | $UUID21 = $UUID.Substring(9, 2) 66 | $UUID22 = $UUID.Substring(11, 2) 67 | 68 | # Section 3 69 | $UUID31 = $UUID.Substring(14, 2) 70 | $UUID32 = $UUID.Substring(16, 2) 71 | 72 | # Section 4 73 | $UUID41 = $UUID.Substring(19, 4) 74 | 75 | # Section 5 76 | $UUID51 = $UUID.Substring(24, 12) 77 | 78 | # Piece the strings together 79 | [string]$UUIDa = "$UUID14$UUID13$UUID12$UUID11" 80 | [string]$UUIDb = "$UUID22$UUID21" 81 | [string]$UUIDc = "$UUID32$UUID31" 82 | [string]$UUIDd = "$UUID41" 83 | [string]$UUIDe = "$UUID51" 84 | [string]$UUIDfixed = "$UUIDa-$UUIDb-$UUIDc-$UUIDd-$UUIDe" 85 | } 86 | 87 | END { 88 | # Return the UUID 89 | Return $UUIDfixed 90 | } 91 | } -------------------------------------------------------------------------------- /vSphere/New-vSphereVM.ps1: -------------------------------------------------------------------------------- 1 | # Load the PowerCLI module 2 | Add-PSSnapin "vmware.VimAutomation.core" -ErrorAction SilentlyContinue 3 | If ((Get-PSSnapin -Registered | Where-Object { $_.Name -eq "VMware.DeployAutomation" }) -eq $null) { 4 | Exit 1 5 | } 6 | 7 | # Configure this PowerCLI session 8 | Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope Session -Confirm:$False -DisplayDeprecationWarnings:$False 9 | 10 | $vCenterHost = "vcenter.home.stealthpuppy.com" 11 | $vmHost = "hv2.home.stealthpuppy.com" 12 | $dataStore = "ILIO_Diskbacked" 13 | $isoPath = "[OCZ Vertex3] ISOs/LiteTouchPE_x86.iso" 14 | $folderLocation = "Others" 15 | $diskSize = 32 16 | $diskStorageFormat = "Thin" 17 | $vCPU = 2 18 | $vRAM = 2 19 | $hardwareVersion = "v8" 20 | 21 | # Get credentials for vCenter and connect 22 | # $MyCredentials = Get-Credential 23 | # Connect-VIServer -Server $vCenterHost -Credential $MyCredentials -Verbose 24 | Connect-VIServer -Server $vCenterHost -User root -Password vmware -Verbose 25 | 26 | # Get virtual machine network 27 | $vmNetwork = Get-VirtualPortGroup | Where-Object { $_.Name -like "*VM Network*" } 28 | 29 | $vmName = Read-Host -Prompt "Enter VM name" 30 | 31 | # Create a new virtual machine 32 | $vm = New-VM -VMHost $vmHost -CD -Datastore $dataStore -DiskGB $diskSize -DiskStorageFormat $diskStorageFormat -Location $folderLocation -MemoryGB $vRAM -Name $vmName -NetworkName $vmNetwork.Name -NumCpu $vCPU -Version $hardwareVersion 33 | 34 | # Add the MDT boot ISO to the new VM 35 | $vm | Get-CDDrive | Set-CDDrive -IsoPath $isoPath -StartConnected:$True -Confirm:$False 36 | 37 | # Change the network adapter type to VMXNET3 38 | $vm | Get-NetworkAdapter | Set-NetworkAdapter -Type Vmxnet3 -NetworkName $vmNetwork.Name -Confirm:$False 39 | 40 | # Change the SCSI adapter to paravirtualized for better performance 41 | $vm | Get-ScsiController | Set-ScsiController -Type ParaVirtual -Confirm:$False 42 | 43 | # Set the OS guest type 44 | $vm | Set-VM -GuestId Windows8Guest -Confirm:$False -------------------------------------------------------------------------------- /vSphere/README.md: -------------------------------------------------------------------------------- 1 | # vSphere 2 | 3 | Scripts for automating the configuration of VMware vSphere. Use at your own risk. 4 | -------------------------------------------------------------------------------- /vSphere/Set-ESXi-PowerPolicy.ps1: -------------------------------------------------------------------------------- 1 | $esxiHost = "hv2.home.stealthpuppy.com" 2 | $vCenterHost = "vcenter.home.stealthpuppy.com" 3 | 4 | Add-PSSnapin "vmware.VimAutomation.core" -ErrorAction SilentlyContinue 5 | Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope Session -Confirm:$False -DisplayDeprecationWarnings:$False 6 | 7 | Connect-VIServer -Server $esxiHost -User root -Password Passw0rd 8 | 9 | Get-VMHost | Sort | select Name, 10 | @{ N = "CurrentPolicy"; E = { $_.ExtensionData.config.PowerSystemInfo.CurrentPolicy.ShortName } }, 11 | @{ N = "CurrentPolicyKey"; E = { $_.ExtensionData.config.PowerSystemInfo.CurrentPolicy.Key } }, 12 | @{ N = "AvailablePolicies"; E = { $_.ExtensionData.config.PowerSystemCapability.AvailablePolicy.ShortName } } 13 | 14 | $view = (Get-VMHost $esxiHost | Get-View) 15 | (Get-View $view.ConfigManager.PowerSystem).ConfigurePowerPolicy(2) 16 | 17 | 18 | 19 | Function Set-VMHostPowerPolicy { 20 | <# 21 | .SYNOPSIS 22 | remove items from folders recursively. 23 | 24 | .DESCRIPTION 25 | this function removes items older than a specified age from the target folder 26 | 27 | .PARAMETER Days 28 | Specifies the ammount of days since the file was last written to you wish to filter on. 29 | 30 | .PARAMETER Path 31 | Specifies the path to the folder you wish to search recursively. 32 | 33 | .PARAMETER Silent 34 | Instructs the function not to return any output. 35 | 36 | .EXAMPLE 37 | PS C:\> remove-itembyage -days 0 -path $recent 38 | 39 | This command searches the $recent directory, for any files, then deletes them. 40 | 41 | .EXAMPLE 42 | PS C:\> remove-itembyage -days 5 -path $recent 43 | 44 | This command searches the $recent directory, for files older than 5 days, then deletes them. 45 | 46 | .EXAMPLE 47 | PS C:\> remove-itembyage -days 10 -path $appdata -typefilter "txt,log" 48 | 49 | This command searches the $cookies directory, for files older than 10 days and end with txt or log extensions, then deletes them. 50 | 51 | .EXAMPLE 52 | PS C:\> remove-itembyage -days 10 -path $cookies -typefilter "txt,log" -silent 53 | 54 | This command searches the $cookies directory, for files older than 10 days and end with txt or log extensions, then deletes them without a report. 55 | 56 | .NOTES 57 | http://blog.stealthpuppy.com/user-virtualization/profile-clean-up-script-powershell-edition/ for support information. 58 | 59 | .LINK 60 | 61 | 62 | #> 63 | 64 | [cmdletbinding(SupportsShouldProcess = $True)] 65 | param( 66 | [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Number of days to filter by, E.G. ""14""")] 67 | [int]$days, 68 | [Parameter(Mandatory = $true, Position = 1, HelpMessage = "Path to files you wish to delete")] 69 | [string]$path, 70 | [string]$typefilter, 71 | [switch]$silent) 72 | 73 | 74 | 75 | $view = (Get-VMHost $esxiHost | Get-View) 76 | (Get-View $view.ConfigManager.PowerSystem).ConfigurePowerPolicy(2) 77 | 78 | } #end function 79 | 80 | 81 | Get-AdvancedSetting -Entity (Get-VMHost $esxiHost) -Name 'Power.CPUPolicy' | Set-AdvancedSetting -Value 'Dynamic' -Confirm:$False 82 | 83 | Set-AdvancedSetting -AdvancedSetting 84 | 85 | Get-AdvancedSetting -Entity (Get-VMHost $esxiHost) | select * | ft -------------------------------------------------------------------------------- /vSphere/vSphere-ControlledMDTDeployment.ps1: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------- 2 | # Controlled MDT deployment 3 | # To create persistent VMs on ILIO 4 | # 5 | # Currently no real error checking 6 | #--------------------------------------------------------------------------- 7 | 8 | # MDT variables 9 | $mdtBootISO = "[ISOs] LiteTouchPE_x86.iso" 10 | $taskSequenceID = "W7ENT-X86" 11 | $deploymentShare = "\\MDT1\Deployment" 12 | $customSettingsINI = "$deploymentShare\Control\CustomSettings.ini" 13 | 14 | # Domain variables 15 | $machineObjectOU = "OU=Workstations,DC=home,DC=stealthpuppy,DC=com" 16 | $dnsDomain = $env:USERDNSDOMAIN.ToLower() 17 | 18 | # vSphere variables 19 | $templateVM_OSName = "W7X86T1" 20 | $templateVM_Name = "Win7-x86-ILIO-Template" 21 | $vSphereTemplate = "ILIO_Win7_x86_Template" 22 | $vCenterHost = "vcenter.home.stealthpuppy.com" 23 | $vmHost = "hv2.home.stealthpuppy.com" 24 | $vmDatastore = "ILIO_VirtualDesktops" 25 | $vmCluster = "ILIO Cluster" 26 | $vmFolder = "ILIO" 27 | 28 | # XenDesktop variables 29 | $adminAddress = "xd71.home.stealthpuppy.com" 30 | $desktopCatalogName = "Windows 7 x86 ILIO" 31 | $desktopGroupName = "Windows 7 ILIO" 32 | $publishedDesktopName = "Windows 7 Desktop" 33 | $timeZone = "GMT Standard Time" 34 | $storageResource = "HV1-LocalStorage" 35 | $hostingUnitPath = "XDHyp:\HostingUnits\UCS-vCenter-SSD" 36 | $hostResource = "Lab SCVMM" 37 | $hostConnectionPath = "XDHyp:\Connections\UCS-vCenter" 38 | 39 | # Persistent VMs 40 | $persistentTargetVMs = @{} 41 | $persistentTargetVMs["W7PERS1"] = @("UCS-POC\W7PERS1", "UCS-POC\aaron") 42 | $persistentTargetVMs["W7PERS2"] = @("UCS-POC\W7PERS2", "UCS-POC\aaron") 43 | $persistentTargetVMs["W7PERS3"] = @("UCS-POC\W7PERS3", "UCS-POC\aaron") 44 | $persistentTargetVMs["W7PERS4"] = @("UCS-POC\W7PERS4", "UCS-POC\aaron") 45 | $persistentTargetVMs["W7PERS5"] = @("UCS-POC\W7PERS5", "UCS-POC\aaron") 46 | 47 | # ILIO variables 48 | $sshHost = "10.130.36.252" 49 | $ilioStoragePath = "/exports/ILIO_VirtualDesktops" 50 | 51 | #--------------------------------------------------------------------------- 52 | 53 | 54 | # Connect to VMware vCenter 55 | Write-Verbose "Importing and configuring PowerCLI." 56 | Add-PSSnapin "vmware.VimAutomation.core" -ErrorAction SilentlyContinue 57 | Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope Session -Confirm:$False -DisplayDeprecationWarnings:$False 58 | Write-Verbose "Getting credentials..." 59 | $MyCredentials = Get-Credential 60 | Write-Verbose "Connecting to vSphere..." 61 | Connect-VIServer -Server $vCenterHost -Credential $MyCredentials -Verbose 62 | 63 | # Remove any existing VM 64 | $targetVM = Get-VM | Where-Object { $_.Name -eq $templateVM_Name } 65 | If ( $targetVM -ne $Null ) { 66 | Write-Verbose "Found existing VM: $targetVM.Name" 67 | If ( $targetVM.PowerState -eq "PoweredOn" ) { Stop-VM -VM $targetVM -Confirm:$False } 68 | Do { 69 | $targetVM = Get-VM | Where-Object { $_.name -eq $templateVM_Name } 70 | Switch ($TargetVM.PowerState) { 71 | { $_ -eq "PoweredOff" } { $Seconds = 0; break } 72 | { $_ -eq "PoweredOn" } { $Seconds = 10; break } 73 | } 74 | Start-Sleep $Seconds 75 | } Until ( $targetVM.PowerState -eq "PoweredOff" ) 76 | Write-Verbose "Removing VM: $targetVM.Name" 77 | Remove-VM -VM $targetVM -DeletePermanently -Confirm:$False 78 | } 79 | 80 | # Create a VM from a template 81 | Write-Verbose "Creating new VM..." 82 | New-VM -VMHost $vmHost -Template $vSphereTemplate -Name $templateVM_Name -Datastore $vmDatastore -Location $vmFolder -Notes "MDT template VM" 83 | 84 | # Get the target VM 85 | Write-Verbose "Getting details for new VM." 86 | $targetVM = Get-VM $templateVM_Name -Verbose 87 | 88 | # Get the target VM"s UUID 89 | Write-Verbose "Retrieving UUID for target VM." 90 | $targetVMUUID = $targetVM | Get-vSphereVMUUID 91 | 92 | #--------------------------------------------------------------------------- 93 | 94 | 95 | # Connect to the MDT share 96 | Write-Verbose "Connecting to MDT." 97 | Add-PSSnapin "Microsoft.BDD.PSSNAPIN" -ErrorAction SilentlyContinue 98 | If (!(Test-Path MDT:)) { New-PSDrive -Name MDT -Root $deploymentShare -PSProvider MDTPROVIDER } 99 | 100 | # Write settings for the target VM to MDT CustomSettings.ini 101 | # open INI file, create or edit section, assign task sequence, configure deployment wizard 102 | Write-Verbose "Backing up CustomSettings.ini." 103 | If (!(Test-Path "$deploymentShare\Control\CustomSettings-Backup.ini")) { Copy-Item $customSettingsINI "$deploymentShare\Control\CustomSettings-Backup.ini" -Force } 104 | 105 | # Add a UUID entry from the target VM to CustomSettings.ini 106 | Write-Verbose "Adding control section for $templateVM_Name." 107 | $Category1 = @{"OSDComputerName" = $templateVM_OSName; "TaskSequenceID" = $taskSequenceID; "MachineObjectOU" = $machineObjectOU; "WindowsUpdate" = "FALSE"; "SkipSummary" = "YES"; "SkipTaskSequence" = "YES"; "SkipApplications" = "YES"; "SkipLocaleSelection" = "YES"; "SkipDomainMembership" = "YES"; "SkipTimeZone" = "YES"; "SkipComputerName" = "YES"; "SkipUserData" = "YES"; "SkipComputerBackup" = "YES"; "SkipFinalSummary" = "YES"; "FinishAction" = "SHUTDOWN" } 108 | $NewINIContent = @{$TargetVMUUID = $Category1 } 109 | Write-Verbose "Writing to CustomSettings.ini." 110 | Out-IniFile -InputObject $NewINIContent -FilePath $customSettingsINI -Force ASCII -Append 111 | 112 | # Clean up the MDT monitor data for the target VM if it exists 113 | Write-Verbose "Clearing MDT monitor data." 114 | Get-MDTMonitorData -Path MDT: | Where-Object { $_.Name -eq $templateVM_OSName } | Remove-MDTMonitorData -Path MDT: 115 | 116 | 117 | # Start the VM 118 | Write-Verbose "Starting $templateVM_Name..." 119 | If (!($TargetVM.PowerState -eq "PoweredOn")) { $targetVM | Start-VM -Verbose } 120 | 121 | # Connect the MDT ISO to the target VM 122 | Write-Verbose "Connecting MDT boot ISO to VM." 123 | $CDDrives = $targetVM.CDDrives 124 | Set-CDDrive -CD $CDDrives -StartConnected:$True -Connected:$True -Confirm:$False 125 | 126 | 127 | # Wait for the OS deployment to start before monitoring 128 | # This may require user intervention to boot the VM from the MDT ISO if an OS exists on the vDisk 129 | If ((Test-Path variable:InProgress) -eq $True) { Remove-Variable -Name InProgress } 130 | Do { 131 | $InProgress = Get-MDTMonitorData -Path MDT: | Where-Object { $_.Name -eq $templateVM_OSName } 132 | If ($InProgress) { 133 | If ($InProgress.PercentComplete -eq 100) { 134 | $Seconds = 30 135 | $tsStarted = $False 136 | Write-Verbose "Waiting for task sequence to begin..." 137 | } 138 | Else { 139 | $Seconds = 0 140 | $tsStarted = $True 141 | Write-Verbose "Task sequence has begun. Moving to monitoring phase." 142 | } 143 | } 144 | Else { 145 | $Seconds = 30 146 | $tsStarted = $False 147 | Write-Verbose "Waiting for task sequence to begin..." 148 | } 149 | Start-Sleep -Seconds $Seconds 150 | } Until ($TSStarted -eq $True) 151 | 152 | # Monitor the MDT OS deployment once started 153 | Write-Verbose "Waiting for task sequence to complete." 154 | If ((Test-Path variable:InProgress) -eq $True) { Remove-Variable -Name InProgress } 155 | Do { 156 | $InProgress = Get-MDTMonitorData -Path MDT: | Where-Object { $_.Name -eq $templateVM_OSName } 157 | If ( $InProgress.PercentComplete -lt 100 ) { 158 | If ( $InProgress.StepName.Length -eq 0 ) { $StatusText = "Waiting for update" } Else { $StatusText = $InProgress.StepName } 159 | Write-Progress -Activity "Task sequence in progress" -Status $StatusText -PercentComplete $InProgress.PercentComplete 160 | Switch ($InProgress.PercentComplete) { 161 | { $_ -lt 25 } { $Seconds = 35; break } 162 | { $_ -lt 50 } { $Seconds = 30; break } 163 | { $_ -lt 75 } { $Seconds = 10; break } 164 | { $_ -lt 100 } { $Seconds = 5; break } 165 | } 166 | Start-Sleep -Seconds $Seconds 167 | } 168 | } Until ($InProgress.CurrentStep -eq $InProgress.TotalSteps) 169 | Write-Verbose "Task sequence complete." 170 | 171 | # OS deployment is complete, ensure the target VM is shutdown 172 | $targetVM = Get-VM | Where-Object { $_.Name -eq $templateVM_Name } 173 | If ( $targetVM -ne $Null ) { 174 | # If ( $targetVM.PowerState -eq "PoweredOn" ) { Shutdown-VMGuest -VM $targetVM -Confirm:$False } 175 | Do { 176 | $targetVM = Get-VM | Where-Object { $_.name -eq $templateVM_Name } 177 | Switch ($TargetVM.PowerState) { 178 | { $_ -eq "PoweredOff" } { $Seconds = 0; break } 179 | { $_ -eq "PoweredOn" } { $Seconds = 10; break } 180 | } 181 | Start-Sleep $Seconds 182 | } Until ( $targetVM.PowerState -eq "PoweredOff" ) 183 | } 184 | 185 | #--------------------------------------------------------------------------- 186 | 187 | 188 | # Connect to the ILIO Session Host to run Fast Clone script 189 | Import-Module SSH-Sessions 190 | Write-Verbose "Connecting to ILIO Session Host." 191 | New-SshSession -ComputerName $sshHost -Username "poweruser" -Password "poweruser" 192 | Write-Verbose "Creating the AD configuration file." 193 | Invoke-SshCommand -ComputerName $sshHost -Command "python /root/iliotools/python_tools/ad_gen.py $ilioStoragePath/$templateVM_Name/ $dnsDomain aaron K3lw4yP0c `"ou=Desktop Virtualization`"" 194 | Write-Verbose "Initiating Fast Clones..." 195 | Invoke-SshCommand -ComputerName $sshHost -Command "/etc/ilio/create-fastclones.sh" 196 | Remove-SshSession -RemoveAll 197 | Write-Verbose "Fast Clones complete." 198 | Start-Sleep 5 199 | 200 | #--------------------------------------------------------------------------- 201 | 202 | # Import the newly cloned VMs into vCenter 203 | foreach ($Datastore in Get-Datastore $vmDatastore) { 204 | Write-Verbose "Searching datastore for new VMs..." 205 | 206 | # Set up Search for .VMX Files in Datastore 207 | $ds = Get-Datastore -Name $Datastore | % { Get-View $_.Id } 208 | $SearchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec 209 | $SearchSpec.matchpattern = "*.vmx" 210 | $dsBrowser = Get-View $ds.browser 211 | $DatastorePath = "[" + $ds.Summary.Name + "]" 212 | 213 | # Find all .VMX file paths in Datastore, filtering out ones with .snapshot (Useful for NetApp NFS) 214 | $SearchResult = $dsBrowser.SearchDatastoreSubFolders($DatastorePath, $SearchSpec) | Where-Object { $_.FolderPath -notmatch ".snapshot" -and $_.FolderPath -notmatch $templateVM_Name } | % { $_.FolderPath + ($_.File | Select-Object Path).Path } 215 | 216 | #Register all .vmx Files as VMs on the datastore 217 | Write-Verbose "Adding VMs to inventory..." 218 | foreach ($VMXFile in $SearchResult) { 219 | New-VM -VMFilePath $VMXFile -VMHost $vmHost -Location $vmFolder -RunAsync 220 | } 221 | } 222 | Start-Sleep 2 223 | 224 | #--------------------------------------------------------------------------- 225 | 226 | # Add XenDesktop snap-ins 227 | Write-Verbose "Adding XenDesktop PowerShell snapins." 228 | Add-PSSnapin Citrix* 229 | 230 | # Get XenDesktop hypervisor connection/host details 231 | Write-Verbose "Getting XenDesktop hypervisor and connection details." 232 | # $hostingUnit = Get-Item -AdminAddress $adminAddress -Path @($hostingUnitPath) 233 | # $hostConnection = Get-Item -AdminAddress $adminAddress -Path @($hostConnectionPath) 234 | $hostingUnit = Get-ChildItem -AdminAddress $adminAddress "XDHyp:\HostingUnits" | Where-Object { $_.PSChildName -like $storageResource } | Select-Object PSChildName, PsPath 235 | $hostConnection = Get-ChildItem -AdminAddress $adminAddress "XDHyp:\Connections" | Where-Object { $_.PSChildName -like $hostResource } | Select-Object PSChildName, PsPath 236 | $brokerHypConnection = Get-BrokerHypervisorConnection -AdminAddress $adminAddress -HypHypervisorConnectionUid $hostConnection.HypervisorConnectionUid 237 | $brokerServiceGroup = Get-ConfigServiceGroup -AdminAddress $adminAddress -ServiceType 'Broker' -MaxRecordCount 2147483647 238 | 239 | # Create a new Desktop Catalog of type Existing 240 | Write-Verbose "Creating XenDesktop catalog." 241 | $brokerCatalog = New-BrokerCatalog -AdminAddress $adminAddress -AllocationType 'Permanent' -CatalogKind 'PowerManaged' -Name $desktopCatalogName 242 | 243 | # Add machines to the catalog contained in the $persistentTargetVMs hashtable 244 | Write-Verbose "Adding ILIO VMs to the catalog..." 245 | $machineIDs = @() 246 | ForEach ( $key in $persistentTargetVMs.Keys ) { 247 | # Get target VM details from vSphere 248 | Write-Verbose "Adding VM: $key..." 249 | $targetVM = Get-ChildItem -Recurse -Path $hostConnection.PSPath | Where-Object { $_.Name -eq $key } 250 | 251 | # Create the machine in the target XD desktop catalog and grant access to a specific user 252 | $brokerMachine = New-BrokerMachine -AdminAddress $adminAddress -CatalogUid $brokerCatalog.Uid -HostedMachineId $targetVM.Id -HypervisorConnectionUid $brokerHypConnection.Uid -MachineName $persistentTargetVMs[$key][0] 253 | Add-BrokerUser -AdminAddress $adminAddress -Name $persistentTargetVMs[$key][1] -Machine $brokerMachine.Uid 254 | 255 | # Add the new machine IDs to an array 256 | $machineIDs += $brokerMachine.Uid 257 | } 258 | Write-Verbose "Desktop catalog complete." 259 | 260 | # Create Desktop Group using the machines from the newly created catalog 261 | Write-Verbose "Creating a Desktop Group and assigning VMs to users..." 262 | $desktopGroup = New-BrokerDesktopGroup -AdminAddress $adminAddress -DesktopKind 'Private' -Name $desktopGroupName -OffPeakBufferSizePercent 10 -PeakBufferSizePercent 10 -PublishedName $publishedDesktopName -ShutdownDesktopsAfterUse $False -TimeZone $timeZone 263 | Add-BrokerMachine -AdminAddress $adminAddress -InputObject @($machineIDs) -DesktopGroup $desktopGroupName 264 | New-BrokerAccessPolicyRule -AdminAddress $adminAddress -AllowedConnections 'NotViaAG' -AllowedProtocols @('RDP', 'HDX') -AllowedUsers 'AnyAuthenticated' -AllowRestart $True -Enabled $True -IncludedDesktopGroupFilterEnabled $True -IncludedDesktopGroups @($desktopGroupName) -IncludedSmartAccessFilterEnabled $True -IncludedUserFilterEnabled $True -Name "$($desktopGroupName)_Direct" 265 | New-BrokerAccessPolicyRule -AdminAddress $adminAddress -AllowedConnections 'ViaAG' -AllowedProtocols @('RDP', 'HDX') -AllowedUsers 'AnyAuthenticated' -AllowRestart $True -Enabled $True -IncludedDesktopGroupFilterEnabled $True -IncludedDesktopGroups @($desktopGroupName) -IncludedSmartAccessFilterEnabled $True -IncludedSmartAccessTags @() -IncludedUserFilterEnabled $True -Name "$($desktopGroupName)_AG" 266 | New-BrokerPowerTimeScheme -AdminAddress $adminAddress -DaysOfWeek 'Weekdays' -DesktopGroupUid $desktopGroup.Uid -DisplayName 'Weekdays' -Name "$($desktopGroupName)_Weekdays" -PeakHours @($False, $False, $False, $False, $False, $False, $False, $True, $True, $True, $True, $True, $True, $True, $True, $True, $True, $True, $True, $False, $False, $False, $False, $False) -PoolSize @(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 267 | New-BrokerPowerTimeScheme -AdminAddress $adminAddress -DaysOfWeek 'Weekend' -DesktopGroupUid $desktopGroup.Uid -DisplayName 'Weekend' -Name "$($desktopGroupName)_Weekend" -PeakHours @($False, $False, $False, $False, $False, $False, $False, $True, $True, $True, $True, $True, $True, $True, $True, $True, $True, $True, $True, $False, $False, $False, $False, $False) -PoolSize @(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 268 | 269 | Write-Verbose "Deployment complete." -------------------------------------------------------------------------------- /vSphere/vSphere-ControlledMDTDeployment.txt: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------- 2 | # Controlled MDT deployment 3 | # To create persistent VMs on ILIO 4 | # 5 | # Currently no real error checking 6 | #--------------------------------------------------------------------------- 7 | 8 | # MDT variables 9 | $mdtBootISO = "[ILIO DataStore] ISOs/MDT/KelwayLiteTouchPE_x86.iso" 10 | $taskSequenceID = "W7ENT-X86" 11 | $deploymentShare = "E:\Deployment" 12 | $customSettingsINI = "$deploymentShare\Control\CustomSettings.ini" 13 | 14 | # Domain variables 15 | $machineObjectOU = "OU=Workstations,DC=home,DC=stealthpuppy,DC=com" 16 | $dnsDomain = $env:USERDNSDOMAIN.ToLower() 17 | 18 | # vSphere variables 19 | $templateVM_OSName = "W7X86T1" 20 | $templateVM_Name = "Win7-x86-ILIO-Template" 21 | $vSphereTemplate = "ILIO_Win7_x86_Template" 22 | $vCenterHost = "vcenter.home.stealthpuppy.com" 23 | $vmHost = "hv2.home.stealthpuppy.com" 24 | $vmDatastore = "ILIO_VirtualDesktops" 25 | $vmCluster = "ILIO Cluster" 26 | $vmFolder = "ILIO" 27 | 28 | # XenDesktop variables 29 | $adminAddress = "xd71.home.stealthpuppy.com" 30 | $desktopCatalogName = "Windows 7 x86 ILIO" 31 | $desktopGroupName = "Windows 7 ILIO" 32 | $publishedDesktopName = "Windows 7 Desktop" 33 | $timeZone = "GMT Standard Time" 34 | $hostingUnitPath = "XDHyp:\HostingUnits\UCS-vCenter-SSD" 35 | $hostConnectionPath = "XDHyp:\Connections\UCS-vCenter" 36 | 37 | # Persistent VMs 38 | $persistentTargetVMs = @{} 39 | $persistentTargetVMs["W7PERS1"] = @("UCS-POC\W7PERS1", "UCS-POC\aaron") 40 | $persistentTargetVMs["W7PERS2"] = @("UCS-POC\W7PERS2", "UCS-POC\aaron") 41 | $persistentTargetVMs["W7PERS3"] = @("UCS-POC\W7PERS3", "UCS-POC\aaron") 42 | $persistentTargetVMs["W7PERS4"] = @("UCS-POC\W7PERS4", "UCS-POC\aaron") 43 | $persistentTargetVMs["W7PERS5"] = @("UCS-POC\W7PERS5", "UCS-POC\aaron") 44 | 45 | # ILIO variables 46 | $sshHost = "10.130.36.252" 47 | $ilioStoragePath = "/exports/ILIO_VirtualDesktops" 48 | 49 | #--------------------------------------------------------------------------- 50 | 51 | 52 | # Connect to VMware vCenter 53 | Write-Host "Importing and configuring PowerCLI." -ForegroundColor Green 54 | Add-PSSnapin "vmware.VimAutomation.core" -ErrorAction SilentlyContinue 55 | Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope Session -Confirm:$False -DisplayDeprecationWarnings:$False 56 | Write-Host "Getting credentials..." -ForegroundColor Green 57 | $MyCredentials = Get-Credential 58 | Write-Host "Connecting to vSphere..." -ForegroundColor Green 59 | Connect-VIServer -Server $vCenterHost -Credential $MyCredentials -Verbose 60 | 61 | # Remove any existing VM 62 | $targetVM = Get-VM | Where-Object { $_.Name -eq $templateVM_Name } 63 | If ( $targetVM -ne $Null ) { 64 | Write-Host "Found existing VM: $targetVM.Name" -ForegroundColor Green 65 | If ( $targetVM.PowerState -eq "PoweredOn" ) { Stop-VM -VM $targetVM -Confirm:$False } 66 | Do { 67 | $targetVM = Get-VM | Where-Object { $_.name -eq $templateVM_Name } 68 | Switch ($TargetVM.PowerState) { 69 | {$_ -eq "PoweredOff"}{$Seconds = 0; break} 70 | {$_ -eq "PoweredOn"}{$Seconds = 10; break} 71 | } 72 | Start-Sleep $Seconds 73 | } Until ( $targetVM.PowerState -eq "PoweredOff" ) 74 | Write-Host "Removing VM: $targetVM.Name" -ForegroundColor Green 75 | Remove-VM -VM $targetVM -DeletePermanently -Confirm:$False 76 | } 77 | 78 | # Create a VM from a template 79 | Write-Host "Creating new VM..." -ForegroundColor Green 80 | New-VM -VMHost $vmHost -Template $vSphereTemplate -Name $templateVM_Name -Datastore $vmDatastore -Location $vmFolder -Notes "MDT template VM" 81 | 82 | # Get the target VM 83 | Write-Host "Getting details for new VM." -ForegroundColor Green 84 | $targetVM = Get-VM $templateVM_Name -Verbose 85 | 86 | # Get the target VM"s UUID 87 | Write-Host "Retrieving UUID for target VM." -ForegroundColor Green 88 | $targetVMUUID = $targetVM | Get-vSphereVMUUID 89 | 90 | #--------------------------------------------------------------------------- 91 | 92 | 93 | # Connect to the MDT share 94 | Write-Host "Connecting to MDT." -ForegroundColor Green 95 | Add-PSSnapin "Microsoft.BDD.PSSNAPIN" -ErrorAction SilentlyContinue 96 | If (!(Test-Path MDT:)) { New-PSDrive -Name MDT -Root $deploymentShare -PSProvider MDTPROVIDER } 97 | 98 | # Write settings for the target VM to MDT CustomSettings.ini 99 | # open INI file, create or edit section, assign task sequence, configure deployment wizard 100 | Write-Host "Backing up CustomSettings.ini." -ForegroundColor Green 101 | If (!(Test-Path "$deploymentShare\Control\CustomSettings-Backup.ini")) { Copy-Item $customSettingsINI "$deploymentShare\Control\CustomSettings-Backup.ini" -Force } 102 | 103 | # Add a UUID entry from the target VM to CustomSettings.ini 104 | Write-Host "Adding control section for $templateVM_Name." -ForegroundColor Green 105 | $Category1 = @{"OSDComputerName"=$templateVM_OSName;"TaskSequenceID"=$taskSequenceID;"MachineObjectOU"=$machineObjectOU;"WindowsUpdate"="FALSE";"SkipSummary"="YES";"SkipTaskSequence"="YES";"SkipApplications"="YES";"SkipLocaleSelection"="YES";"SkipDomainMembership"="YES";"SkipTimeZone"="YES";"SkipComputerName"="YES";"SkipUserData"="YES";"SkipComputerBackup"="YES";"SkipFinalSummary"="YES";"FinishAction"="SHUTDOWN"} 106 | $NewINIContent = @{$TargetVMUUID=$Category1} 107 | Write-Host "Writing to CustomSettings.ini." -ForegroundColor Green 108 | Out-IniFile -InputObject $NewINIContent -FilePath $customSettingsINI -Force ASCII -Append 109 | 110 | # Clean up the MDT monitor data for the target VM if it exists 111 | Write-Host "Clearing MDT monitor data." -ForegroundColor Green 112 | Get-MDTMonitorData -Path MDT: | Where-Object { $_.Name -eq $templateVM_OSName } | Remove-MDTMonitorData -Path MDT: 113 | 114 | 115 | # Start the VM 116 | Write-Host "Starting $templateVM_Name..." -ForegroundColor Green 117 | If (!($TargetVM.PowerState -eq "PoweredOn")) { $targetVM | Start-VM -Verbose } 118 | 119 | # Connect the MDT ISO to the target VM 120 | Write-Host "Connecting MDT boot ISO to VM." -ForegroundColor Green 121 | $CDDrives = $targetVM.CDDrives 122 | Set-CDDrive -CD $CDDrives -StartConnected:$True -Connected:$True -Confirm:$False 123 | 124 | 125 | # Wait for the OS deployment to start before monitoring 126 | # This may require user intervention to boot the VM from the MDT ISO if an OS exists on the vDisk 127 | If ((Test-Path variable:InProgress) -eq $True) { Remove-Variable -Name InProgress } 128 | Do { 129 | $InProgress = Get-MDTMonitorData -Path MDT: | Where-Object { $_.Name -eq $templateVM_OSName } 130 | If ($InProgress) { 131 | If ($InProgress.PercentComplete -eq 100) { 132 | $Seconds = 30 133 | $tsStarted = $False 134 | Write-Host "Waiting for task sequence to begin..." -ForegroundColor Green 135 | } Else { 136 | $Seconds = 0 137 | $tsStarted = $True 138 | Write-Host "Task sequence has begun. Moving to monitoring phase." -ForegroundColor Green 139 | } 140 | } Else { 141 | $Seconds = 30 142 | $tsStarted = $False 143 | Write-Host "Waiting for task sequence to begin..." -ForegroundColor Green 144 | } 145 | Start-Sleep -Seconds $Seconds 146 | } Until ($TSStarted -eq $True) 147 | 148 | # Monitor the MDT OS deployment once started 149 | Write-Host "Waiting for task sequence to complete." -ForegroundColor Green 150 | If ((Test-Path variable:InProgress) -eq $True) { Remove-Variable -Name InProgress } 151 | Do { 152 | $InProgress = Get-MDTMonitorData -Path MDT: | Where-Object { $_.Name -eq $templateVM_OSName } 153 | If ( $InProgress.PercentComplete -lt 100 ) { 154 | If ( $InProgress.StepName.Length -eq 0 ) { $StatusText = "Waiting for update" } Else { $StatusText = $InProgress.StepName } 155 | Write-Progress -Activity "Task sequence in progress" -Status $StatusText -PercentComplete $InProgress.PercentComplete 156 | Switch ($InProgress.PercentComplete) { 157 | {$_ -lt 25}{$Seconds = 35; break} 158 | {$_ -lt 50}{$Seconds = 30; break} 159 | {$_ -lt 75}{$Seconds = 10; break} 160 | {$_ -lt 100}{$Seconds = 5; break} 161 | } 162 | Start-Sleep -Seconds $Seconds 163 | } 164 | } Until ($InProgress.CurrentStep -eq $InProgress.TotalSteps) 165 | Write-Host "Task sequence complete." -ForegroundColor Green 166 | 167 | # OS deployment is complete, ensure the target VM is shutdown 168 | $targetVM = Get-VM | Where-Object { $_.Name -eq $templateVM_Name } 169 | If ( $targetVM -ne $Null ) { 170 | # If ( $targetVM.PowerState -eq "PoweredOn" ) { Shutdown-VMGuest -VM $targetVM -Confirm:$False } 171 | Do { 172 | $targetVM = Get-VM | Where-Object { $_.name -eq $templateVM_Name } 173 | Switch ($TargetVM.PowerState) { 174 | {$_ -eq "PoweredOff"}{$Seconds = 0; break} 175 | {$_ -eq "PoweredOn"}{$Seconds = 10; break} 176 | } 177 | Start-Sleep $Seconds 178 | } Until ( $targetVM.PowerState -eq "PoweredOff" ) 179 | } 180 | 181 | #--------------------------------------------------------------------------- 182 | 183 | 184 | # Connect to the ILIO Session Host to run Fast Clone script 185 | Import-Module SSH-Sessions 186 | Write-Host "Connecting to ILIO Session Host." -ForegroundColor Green 187 | New-SshSession -ComputerName $sshHost -Username "poweruser" -Password "poweruser" 188 | Write-Host "Creating the AD configuration file." -ForegroundColor Green 189 | Invoke-SshCommand -ComputerName $sshHost -Command "python /root/iliotools/python_tools/ad_gen.py $ilioStoragePath/$templateVM_Name/ $dnsDomain aaron K3lw4yP0c `"ou=Desktop Virtualization`"" 190 | Write-Host "Initiating Fast Clones..." -ForegroundColor Green 191 | Invoke-SshCommand -ComputerName $sshHost -Command "/etc/ilio/create-fastclones.sh" 192 | Remove-SshSession -RemoveAll 193 | Write-Host "Fast Clones complete." -ForegroundColor Green 194 | Start-Sleep 5 195 | 196 | #--------------------------------------------------------------------------- 197 | 198 | # Import the newly cloned VMs into vCenter 199 | foreach($Datastore in Get-Datastore $vmDatastore) { 200 | Write-Host "Searching datastore for new VMs..." -ForegroundColor Green 201 | 202 | # Set up Search for .VMX Files in Datastore 203 | $ds = Get-Datastore -Name $Datastore | %{Get-View $_.Id} 204 | $SearchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec 205 | $SearchSpec.matchpattern = "*.vmx" 206 | $dsBrowser = Get-View $ds.browser 207 | $DatastorePath = "[" + $ds.Summary.Name + "]" 208 | 209 | # Find all .VMX file paths in Datastore, filtering out ones with .snapshot (Useful for NetApp NFS) 210 | $SearchResult = $dsBrowser.SearchDatastoreSubFolders($DatastorePath, $SearchSpec) | where {$_.FolderPath -notmatch ".snapshot" -and $_.FolderPath -notmatch $templateVM_Name } | %{$_.FolderPath + ($_.File | select Path).Path} 211 | 212 | #Register all .vmx Files as VMs on the datastore 213 | Write-Host "Adding VMs to inventory..." -ForegroundColor Green 214 | foreach($VMXFile in $SearchResult) { 215 | New-VM -VMFilePath $VMXFile -VMHost $vmHost -Location $vmFolder -RunAsync 216 | } 217 | } 218 | Start-Sleep 2 219 | 220 | #--------------------------------------------------------------------------- 221 | 222 | # Add XenDesktop snap-ins 223 | Write-Host "Adding XenDesktop PowerShell snapins." -ForegroundColor Green 224 | Add-PSSnapin Citrix* 225 | 226 | # Get XenDesktop hypervisor connection/host details 227 | Write-Host "Getting XenDesktop hypervisor and connection details." -ForegroundColor Green 228 | $hostingUnit = Get-Item -AdminAddress $adminAddress -Path @($hostingUnitPath) 229 | $hostConnection = Get-Item -AdminAddress $adminAddress -Path @($hostConnectionPath) 230 | $brokerHypConnection = Get-BrokerHypervisorConnection -AdminAddress $adminAddress -HypHypervisorConnectionUid $hostConnection.HypervisorConnectionUid 231 | $brokerServiceGroup = Get-ConfigServiceGroup -AdminAddress $adminAddress -ServiceType 'Broker' -MaxRecordCount 2147483647 232 | 233 | # Create a new Desktop Catalog of type Existing 234 | Write-Host "Creating XenDesktop catalog." -ForegroundColor Green 235 | $brokerCatalog = New-BrokerCatalog -AdminAddress $adminAddress -AllocationType 'Permanent' -CatalogKind 'PowerManaged' -Name $desktopCatalogName 236 | 237 | # Add machines to the catalog contained in the $persistentTargetVMs hashtable 238 | Write-Host "Adding ILIO VMs to the catalog..." -ForegroundColor Green 239 | $machineIDs = @() 240 | ForEach ( $key in $persistentTargetVMs.Keys ) { 241 | # Get target VM details from vSphere 242 | Write-Host "Adding VM: $key..." -ForegroundColor Green 243 | $targetVM = Get-ChildItem -Recurse -Path $hostConnection.PSPath | Where-Object { $_.Name -eq $key } 244 | 245 | # Create the machine in the target XD desktop catalog and grant access to a specific user 246 | $brokerMachine = New-BrokerMachine -AdminAddress $adminAddress -CatalogUid $brokerCatalog.Uid -HostedMachineId $targetVM.Id -HypervisorConnectionUid $brokerHypConnection.Uid -MachineName $persistentTargetVMs[$key][0] 247 | Add-BrokerUser -AdminAddress $adminAddress -Name $persistentTargetVMs[$key][1] -Machine $brokerMachine.Uid 248 | 249 | # Add the new machine IDs to an array 250 | $machineIDs += $brokerMachine.Uid 251 | } 252 | Write-Host "Desktop catalog complete." -ForegroundColor Green 253 | 254 | # Create Desktop Group using the machines from the newly created catalog 255 | Write-Host "Creating a Desktop Group and assigning VMs to users..." -ForegroundColor Green 256 | $desktopGroup = New-BrokerDesktopGroup -AdminAddress $adminAddress -DesktopKind 'Private' -Name $desktopGroupName -OffPeakBufferSizePercent 10 -PeakBufferSizePercent 10 -PublishedName $publishedDesktopName -ShutdownDesktopsAfterUse $False -TimeZone $timeZone 257 | Add-BrokerMachine -AdminAddress $adminAddress -InputObject @($machineIDs) -DesktopGroup $desktopGroupName 258 | New-BrokerAccessPolicyRule -AdminAddress $adminAddress -AllowedConnections 'NotViaAG' -AllowedProtocols @('RDP','HDX') -AllowedUsers 'AnyAuthenticated' -AllowRestart $True -Enabled $True -IncludedDesktopGroupFilterEnabled $True -IncludedDesktopGroups @($desktopGroupName) -IncludedSmartAccessFilterEnabled $True -IncludedUserFilterEnabled $True -Name "$($desktopGroupName)_Direct" 259 | New-BrokerAccessPolicyRule -AdminAddress $adminAddress -AllowedConnections 'ViaAG' -AllowedProtocols @('RDP','HDX') -AllowedUsers 'AnyAuthenticated' -AllowRestart $True -Enabled $True -IncludedDesktopGroupFilterEnabled $True -IncludedDesktopGroups @($desktopGroupName) -IncludedSmartAccessFilterEnabled $True -IncludedSmartAccessTags @() -IncludedUserFilterEnabled $True -Name "$($desktopGroupName)_AG" 260 | New-BrokerPowerTimeScheme -AdminAddress $adminAddress -DaysOfWeek 'Weekdays' -DesktopGroupUid $desktopGroup.Uid -DisplayName 'Weekdays' -Name "$($desktopGroupName)_Weekdays" -PeakHours @($False,$False,$False,$False,$False,$False,$False,$True,$True,$True,$True,$True,$True,$True,$True,$True,$True,$True,$True,$False,$False,$False,$False,$False) -PoolSize @(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) 261 | New-BrokerPowerTimeScheme -AdminAddress $adminAddress -DaysOfWeek 'Weekend' -DesktopGroupUid $desktopGroup.Uid -DisplayName 'Weekend' -Name "$($desktopGroupName)_Weekend" -PeakHours @($False,$False,$False,$False,$False,$False,$False,$True,$True,$True,$True,$True,$True,$True,$True,$True,$True,$True,$True,$False,$False,$False,$False,$False) -PoolSize @(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) 262 | 263 | Write-Host "Deployment complete." -ForegroundColor Green -------------------------------------------------------------------------------- /vSphere/vSphere-ControlledMDTDeployment_v2.ps1: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------- 2 | # Controlled MDT deployment 3 | #--------------------------------------------------------------------------- 4 | 5 | 6 | #--------------------------------------------------------------------------- 7 | # vSphere variables 8 | $templateVM_OSName = "W7X86T1" 9 | $templateVM_Name = "Win7-x86-ILIO-Template" 10 | $vSphereTemplate = "ILIO_Win7_x86_Template" 11 | $vCenterHost = "vcenter.home.stealthpuppy.com" 12 | $vmHost = "hv2.home.stealthpuppy.com" 13 | $vmDatastore = "ILIO_VirtualDesktops" 14 | $vmCluster = "ILIO Cluster" 15 | $vmFolder = "ILIO" 16 | 17 | # Connect to VMware vCenter 18 | Write-Verbose "Importing and configuring PowerCLI." 19 | Add-PSSnapin "vmware.VimAutomation.core" -ErrorAction SilentlyContinue 20 | Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope Session -Confirm:$False -DisplayDeprecationWarnings:$False 21 | Write-Verbose "Getting credentials..." 22 | $MyCredentials = Get-Credential 23 | Write-Verbose "Connecting to vSphere..." 24 | Connect-VIServer -Server $vCenterHost -Credential $MyCredentials -Verbose 25 | 26 | # Remove any existing VM 27 | $targetVM = Get-VM | Where-Object { $_.Name -eq $templateVM_Name } 28 | If ( $targetVM -ne $Null ) { 29 | Write-Verbose "Found existing VM: $targetVM.Name" 30 | If ( $targetVM.PowerState -eq "PoweredOn" ) { Stop-VM -VM $targetVM -Confirm:$False } 31 | Do { 32 | $targetVM = Get-VM | Where-Object { $_.name -eq $templateVM_Name } 33 | Switch ($TargetVM.PowerState) { 34 | { $_ -eq "PoweredOff" } { $Seconds = 0; break } 35 | { $_ -eq "PoweredOn" } { $Seconds = 10; break } 36 | } 37 | Start-Sleep $Seconds 38 | } Until ( $targetVM.PowerState -eq "PoweredOff" ) 39 | Write-Verbose "Removing VM: $targetVM.Name" 40 | Remove-VM -VM $targetVM -DeletePermanently -Confirm:$False 41 | } 42 | 43 | # Create a VM from a template 44 | Write-Verbose "Creating new VM..." 45 | New-VM -VMHost $vmHost -Template $vSphereTemplate -Name $templateVM_Name -Datastore $vmDatastore -Location $vmFolder -Notes "MDT template VM" 46 | 47 | # Get the target VM 48 | Write-Verbose "Getting details for new VM." 49 | $targetVM = Get-VM $templateVM_Name -Verbose 50 | 51 | # Get the target VM"s UUID 52 | Write-Verbose "Retrieving UUID for target VM." 53 | $targetVMUUID = $targetVM | Get-vSphereVMUUID 54 | 55 | 56 | #--------------------------------------------------------------------------- 57 | # MDT variables 58 | $mdtBootISO = "[ISOs] LiteTouchPE_x86.iso" 59 | $taskSequenceID = "W7ENT-X86" 60 | $deploymentShare = "\\MDT1\Deployment" 61 | $customSettingsINI = "$deploymentShare\Control\CustomSettings.ini" 62 | $machineObjectOU = "OU=Workstations,DC=home,DC=stealthpuppy,DC=com" 63 | 64 | # Connect to the MDT share 65 | Write-Verbose "Connecting to MDT." 66 | Add-PSSnapin "Microsoft.BDD.PSSNAPIN" -ErrorAction SilentlyContinue 67 | If (!(Test-Path MDT:)) { New-PSDrive -Name MDT -Root $deploymentShare -PSProvider MDTPROVIDER } 68 | 69 | # Write settings for the target VM to MDT CustomSettings.ini 70 | # open INI file, create or edit section, assign task sequence, configure deployment wizard 71 | Write-Verbose "Backing up CustomSettings.ini." 72 | If (!(Test-Path "$deploymentShare\Control\CustomSettings-Backup.ini")) { Copy-Item $customSettingsINI "$deploymentShare\Control\CustomSettings-Backup.ini" -Force } 73 | 74 | # Add a UUID entry from the target VM to CustomSettings.ini 75 | Write-Verbose "Adding control section for $templateVM_Name." 76 | $ini = Get-IniFile -FilePath $customSettingsINI 77 | $ini.Add($TargetVMUUID, (New-Object System.Collections.Specialized.OrderedDictionary)) 78 | $ini.$TargetVMUUID.Add("OSDComputerName", $templateVM_OSName) 79 | $ini.$TargetVMUUID.Add("TaskSequenceID", $taskSequenceID) 80 | $ini.$TargetVMUUID.Add("MachineObjectOU", $machineObjectOU) 81 | $ini.$TargetVMUUID.Add("WindowsUpdate", "FALSE") 82 | $ini.$TargetVMUUID.Add("SkipSummary", "YES") 83 | $ini.$TargetVMUUID.Add("SkipTaskSequence", "YES") 84 | $ini.$TargetVMUUID.Add("SkipApplications", "YES") 85 | $ini.$TargetVMUUID.Add("SkipLocaleSelection", "YES") 86 | $ini.$TargetVMUUID.Add("SkipDomainMembership", "YES") 87 | $ini.$TargetVMUUID.Add("SkipTimeZone", "YES") 88 | $ini.$TargetVMUUID.Add("SkipComputerName", "YES") 89 | $ini.$TargetVMUUID.Add("SkipUserData", "YES") 90 | $ini.$TargetVMUUID.Add("SkipComputerBackup", "YES") 91 | $ini.$TargetVMUUID.Add("SkipFinalSummary", "YES") 92 | $ini.$TargetVMUUID.Add("FinishAction", "SHUTDOWN") 93 | Write-Verbose "Writing to CustomSettings.ini." 94 | $ini | Out-Ini -FilePath $customSettingsINI 95 | 96 | # Clean up the MDT monitor data for the target VM if it exists 97 | Write-Verbose "Clearing MDT monitor data." 98 | Get-MDTMonitorData -Path MDT: | Where-Object { $_.Name -eq $templateVM_OSName } | Remove-MDTMonitorData -Path MDT: 99 | 100 | 101 | # Start the VM 102 | Write-Verbose "Starting $templateVM_Name..." 103 | If (!($TargetVM.PowerState -eq "PoweredOn")) { $targetVM | Start-VM -Verbose } 104 | 105 | # Connect the MDT ISO to the target VM 106 | Write-Verbose "Connecting MDT boot ISO to VM." 107 | $CDDrives = $targetVM.CDDrives 108 | Set-CDDrive -CD $CDDrives -StartConnected:$True -Connected:$True -Confirm:$False 109 | 110 | 111 | # Wait for the OS deployment to start before monitoring 112 | # This may require user intervention to boot the VM from the MDT ISO if an OS exists on the vDisk 113 | If ((Test-Path variable:InProgress) -eq $True) { Remove-Variable -Name InProgress } 114 | Do { 115 | $InProgress = Get-MDTMonitorData -Path MDT: | Where-Object { $_.Name -eq $templateVM_OSName } 116 | If ($InProgress) { 117 | If ($InProgress.PercentComplete -eq 100) { 118 | $Seconds = 30 119 | $tsStarted = $False 120 | Write-Verbose "Waiting for task sequence to begin..." 121 | } 122 | Else { 123 | $Seconds = 0 124 | $tsStarted = $True 125 | Write-Verbose "Task sequence has begun. Moving to monitoring phase." 126 | } 127 | } 128 | Else { 129 | $Seconds = 30 130 | $tsStarted = $False 131 | Write-Verbose "Waiting for task sequence to begin..." 132 | } 133 | Start-Sleep -Seconds $Seconds 134 | } Until ($TSStarted -eq $True) 135 | 136 | # Monitor the MDT OS deployment once started 137 | Write-Verbose "Waiting for task sequence to complete." 138 | If ((Test-Path variable:InProgress) -eq $True) { Remove-Variable -Name InProgress } 139 | Do { 140 | $InProgress = Get-MDTMonitorData -Path MDT: | Where-Object { $_.Name -eq $templateVM_OSName } 141 | If ( $InProgress.PercentComplete -lt 100 ) { 142 | If ( $InProgress.StepName.Length -eq 0 ) { $StatusText = "Waiting for update" } Else { $StatusText = $InProgress.StepName } 143 | Write-Progress -Activity "Task sequence in progress" -Status $StatusText -PercentComplete $InProgress.PercentComplete 144 | Switch ($InProgress.PercentComplete) { 145 | { $_ -lt 25 } { $Seconds = 35; break } 146 | { $_ -lt 50 } { $Seconds = 30; break } 147 | { $_ -lt 75 } { $Seconds = 10; break } 148 | { $_ -lt 100 } { $Seconds = 5; break } 149 | } 150 | Start-Sleep -Seconds $Seconds 151 | } 152 | } Until ($InProgress.CurrentStep -eq $InProgress.TotalSteps) 153 | Write-Verbose "Task sequence complete." 154 | 155 | # OS deployment is complete, ensure the target VM is shutdown 156 | $targetVM = Get-VM | Where-Object { $_.Name -eq $templateVM_Name } 157 | If ( $targetVM -ne $Null ) { 158 | # If ( $targetVM.PowerState -eq "PoweredOn" ) { Shutdown-VMGuest -VM $targetVM -Confirm:$False } 159 | Do { 160 | $targetVM = Get-VM | Where-Object { $_.name -eq $templateVM_Name } 161 | Switch ($TargetVM.PowerState) { 162 | { $_ -eq "PoweredOff" } { $Seconds = 0; break } 163 | { $_ -eq "PoweredOn" } { $Seconds = 10; break } 164 | } 165 | Start-Sleep $Seconds 166 | } Until ( $targetVM.PowerState -eq "PoweredOff" ) 167 | } 168 | 169 | 170 | #--------------------------------------------------------------------------- 171 | # ILIO variables 172 | $sshHost = "10.130.36.252" 173 | $ilioStoragePath = "/exports/ILIO_VirtualDesktops" 174 | $dnsDomain = $env:USERDNSDOMAIN.ToLower() 175 | 176 | # Connect to the ILIO Session Host to run Fast Clone script 177 | Import-Module SSH-Sessions 178 | Write-Verbose "Connecting to ILIO Session Host." 179 | New-SshSession -ComputerName $sshHost -Username "poweruser" -Password "poweruser" 180 | Write-Verbose "Creating the AD configuration file." 181 | Invoke-SshCommand -ComputerName $sshHost -Command "python /root/iliotools/python_tools/ad_gen.py $ilioStoragePath/$templateVM_Name/ $dnsDomain aaron K3lw4yP0c `"ou=Desktop Virtualization`"" 182 | Write-Verbose "Initiating Fast Clones..." 183 | Invoke-SshCommand -ComputerName $sshHost -Command "/etc/ilio/create-fastclones.sh" 184 | Remove-SshSession -RemoveAll 185 | Write-Verbose "Fast Clones complete." 186 | Start-Sleep 5 187 | 188 | 189 | #--------------------------------------------------------------------------- 190 | # Import the newly cloned VMs into vCenter 191 | # http://www.wooditwork.com/2011/08/11/adding-vmx-files-to-vcenter-inventory-with-powercli-gets-even-easier/ 192 | foreach ($Datastore in Get-Datastore $vmDatastore) { 193 | Write-Verbose "Searching datastore for new VMs..." 194 | 195 | # Set up Search for .VMX Files in Datastore 196 | $ds = Get-Datastore -Name $Datastore | % { Get-View $_.Id } 197 | $SearchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec 198 | $SearchSpec.matchpattern = "*.vmx" 199 | $dsBrowser = Get-View $ds.browser 200 | $DatastorePath = "[" + $ds.Summary.Name + "]" 201 | 202 | # Find all .VMX file paths in Datastore, filtering out ones with .snapshot (Useful for NetApp NFS) 203 | $SearchResult = $dsBrowser.SearchDatastoreSubFolders($DatastorePath, $SearchSpec) | Where-Object { $_.FolderPath -notmatch ".snapshot" -and $_.FolderPath -notmatch $templateVM_Name } | % { $_.FolderPath + ($_.File | Select-Object Path).Path } 204 | 205 | #Register all .vmx Files as VMs on the datastore 206 | Write-Verbose "Adding VMs to inventory..." 207 | foreach ($VMXFile in $SearchResult) { 208 | New-VM -VMFilePath $VMXFile -VMHost $vmHost -Location $vmFolder -RunAsync 209 | } 210 | } 211 | Start-Sleep 2 212 | 213 | 214 | #--------------------------------------------------------------------------- 215 | # XenDesktop variables 216 | $adminAddress = "xd71.home.stealthpuppy.com" 217 | $desktopCatalogName = "Windows 7 x86 ILIO" 218 | $desktopGroupName = "Windows 7 ILIO" 219 | $publishedDesktopName = "Windows 7 Desktop" 220 | $timeZone = "GMT Standard Time" 221 | $storageResource = "HV1-LocalStorage" 222 | $hostingUnitPath = "XDHyp:\HostingUnits\UCS-vCenter-SSD" 223 | $hostResource = "Lab SCVMM" 224 | $hostConnectionPath = "XDHyp:\Connections\UCS-vCenter" 225 | 226 | # Persistent VMs 227 | $persistentTargetVMs = @{} 228 | $persistentTargetVMs["W7PERS1"] = @("UCS-POC\W7PERS1", "UCS-POC\aaron") 229 | $persistentTargetVMs["W7PERS2"] = @("UCS-POC\W7PERS2", "UCS-POC\aaron") 230 | $persistentTargetVMs["W7PERS3"] = @("UCS-POC\W7PERS3", "UCS-POC\aaron") 231 | $persistentTargetVMs["W7PERS4"] = @("UCS-POC\W7PERS4", "UCS-POC\aaron") 232 | $persistentTargetVMs["W7PERS5"] = @("UCS-POC\W7PERS5", "UCS-POC\aaron") 233 | 234 | # Add XenDesktop snap-ins 235 | Write-Verbose "Adding XenDesktop PowerShell snapins." 236 | Add-PSSnapin Citrix* 237 | 238 | # Get XenDesktop hypervisor connection/host details 239 | Write-Verbose "Getting XenDesktop hypervisor and connection details." 240 | # $hostingUnit = Get-Item -AdminAddress $adminAddress -Path @($hostingUnitPath) 241 | # $hostConnection = Get-Item -AdminAddress $adminAddress -Path @($hostConnectionPath) 242 | $hostingUnit = Get-ChildItem -AdminAddress $adminAddress "XDHyp:\HostingUnits" | Where-Object { $_.PSChildName -like $storageResource } | Select-Object PSChildName, PsPath 243 | $hostConnection = Get-ChildItem -AdminAddress $adminAddress "XDHyp:\Connections" | Where-Object { $_.PSChildName -like $hostResource } | Select-Object PSChildName, PsPath 244 | $brokerHypConnection = Get-BrokerHypervisorConnection -AdminAddress $adminAddress -HypHypervisorConnectionUid $hostConnection.HypervisorConnectionUid 245 | $brokerServiceGroup = Get-ConfigServiceGroup -AdminAddress $adminAddress -ServiceType 'Broker' -MaxRecordCount 2147483647 246 | 247 | # Create a new Desktop Catalog of type Existing 248 | Write-Verbose "Creating XenDesktop catalog." 249 | $brokerCatalog = New-BrokerCatalog -AdminAddress $adminAddress -AllocationType 'Permanent' -CatalogKind 'PowerManaged' -Name $desktopCatalogName 250 | 251 | # Add machines to the catalog contained in the $persistentTargetVMs hashtable 252 | Write-Verbose "Adding ILIO VMs to the catalog..." 253 | $machineIDs = @() 254 | ForEach ( $key in $persistentTargetVMs.Keys ) { 255 | # Get target VM details from vSphere 256 | Write-Verbose "Adding VM: $key..." 257 | $targetVM = Get-ChildItem -Recurse -Path $hostConnection.PSPath | Where-Object { $_.Name -eq $key } 258 | 259 | # Create the machine in the target XD desktop catalog and grant access to a specific user 260 | $brokerMachine = New-BrokerMachine -AdminAddress $adminAddress -CatalogUid $brokerCatalog.Uid -HostedMachineId $targetVM.Id -HypervisorConnectionUid $brokerHypConnection.Uid -MachineName $persistentTargetVMs[$key][0] 261 | Add-BrokerUser -AdminAddress $adminAddress -Name $persistentTargetVMs[$key][1] -Machine $brokerMachine.Uid 262 | 263 | # Add the new machine IDs to an array 264 | $machineIDs += $brokerMachine.Uid 265 | } 266 | Write-Verbose "Desktop catalog complete." 267 | 268 | # Create Desktop Group using the machines from the newly created catalog 269 | Write-Verbose "Creating a Desktop Group and assigning VMs to users..." 270 | $desktopGroup = New-BrokerDesktopGroup -AdminAddress $adminAddress -DesktopKind 'Private' -Name $desktopGroupName -OffPeakBufferSizePercent 10 -PeakBufferSizePercent 10 -PublishedName $publishedDesktopName -ShutdownDesktopsAfterUse $False -TimeZone $timeZone 271 | Add-BrokerMachine -AdminAddress $adminAddress -InputObject @($machineIDs) -DesktopGroup $desktopGroupName 272 | New-BrokerAccessPolicyRule -AdminAddress $adminAddress -AllowedConnections 'NotViaAG' -AllowedProtocols @('RDP', 'HDX') -AllowedUsers 'AnyAuthenticated' -AllowRestart $True -Enabled $True -IncludedDesktopGroupFilterEnabled $True -IncludedDesktopGroups @($desktopGroupName) -IncludedSmartAccessFilterEnabled $True -IncludedUserFilterEnabled $True -Name "$($desktopGroupName)_Direct" 273 | New-BrokerAccessPolicyRule -AdminAddress $adminAddress -AllowedConnections 'ViaAG' -AllowedProtocols @('RDP', 'HDX') -AllowedUsers 'AnyAuthenticated' -AllowRestart $True -Enabled $True -IncludedDesktopGroupFilterEnabled $True -IncludedDesktopGroups @($desktopGroupName) -IncludedSmartAccessFilterEnabled $True -IncludedSmartAccessTags @() -IncludedUserFilterEnabled $True -Name "$($desktopGroupName)_AG" 274 | New-BrokerPowerTimeScheme -AdminAddress $adminAddress -DaysOfWeek 'Weekdays' -DesktopGroupUid $desktopGroup.Uid -DisplayName 'Weekdays' -Name "$($desktopGroupName)_Weekdays" -PeakHours @($False, $False, $False, $False, $False, $False, $False, $True, $True, $True, $True, $True, $True, $True, $True, $True, $True, $True, $True, $False, $False, $False, $False, $False) -PoolSize @(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 275 | New-BrokerPowerTimeScheme -AdminAddress $adminAddress -DaysOfWeek 'Weekend' -DesktopGroupUid $desktopGroup.Uid -DisplayName 'Weekend' -Name "$($desktopGroupName)_Weekend" -PeakHours @($False, $False, $False, $False, $False, $False, $False, $True, $True, $True, $True, $True, $True, $True, $True, $True, $True, $True, $True, $False, $False, $False, $False, $False) -PoolSize @(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 276 | 277 | Write-Verbose "Deployment complete." --------------------------------------------------------------------------------