functions/public.ps1


#these are functions to be exported and visible to the user

#todo - Get-VHDSummary
Function Expand-VMGroup {
    [cmdletbinding()]
    [outputtype("myGroupVM","String")]

    Param(
        [Parameter(Position = 0, ValueFromPipeline)]
        [string]$Name,
        [ValidateNotNullorEmpty()]
        [string]$Computername = $ENV:COMPUTERNAME,
        [pscredential]$Credential,
        [ValidateSet("VMCollectionType", "ManagementCollectionType")]
        [string]$GroupType,
        [switch]$List
    )
    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)"
    } #begin

    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Getting VMGroups from $Computername"
        if ($Name) {
            Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Filtering groups by name: $Name"
        }

        #remove these from psboundparameters
        "list", "grouptype" | foreach-object {
            if ($PSBoundParameters.ContainsKey($_)) {
                [void]$PSBoundParameters.remove($_)
            }
        }
        Try {
            $groups = Get-VMGroup @PSBoundParameters -ErrorAction stop
            if ($GroupType) {
                Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Filtering groups by type: $GroupType"
                $groups = $groups.where( {$_.GroupType -eq $GroupType})
            }
        }
        Catch {
            throw $_
        }
        if ($Groups) {
            Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Found $($groups.count) matching VM group(s)"
            #initialize an array to keep track of vm names.
            $names = @()
            foreach ($group in $groups) {
                if ($group.grouptype -eq 'ManagementCollectionType') {
                    $members = $group.VMGroupMembers.VMMembers
                }
                else {
                    $members = $group.VMMembers
                }

                foreach ($item in $members) {
                    if ($List) {
                        #only write the VMName to the pipeline
                        #if it hasn't been used before
                        if ($names -notcontains $item.name) {
                            $item.name
                        }
                        $names += $item.name
                    }
                    else {
                        #write a custom object to the pipeline
                        [pscustomobject]@{
                            PSTypeName   = "myGroupVM"
                            VMGroup      = $group.name
                            Name         = $item.name
                            State        = $item.State
                            Uptime       = $item.Uptime
                            Status       = $item.Status
                            Computername = $item.Computername
                        }
                    } #else
                } #foreach item

            } #foreach Group
        } #if
        else {
            Write-Warning "No matching VM Groups found."
        }
    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)"
    } #end

} #close Expand-VMGroup

Function Start-VMGroup {
    [cmdletbinding(SupportsShouldProcess)]
    [Outputtype("None", "Microsoft.HyperV.PowerShell.VirtualMachine", "Microsoft.HyperV.PowerShell.Commands.VmJob")]
    Param(
        [Parameter(Position = 0, ValueFromPipeline, HelpMessage = "The name of your VM Group")]
        [string]$Name,
        [ValidateNotNullorEmpty()]
        [string]$Computername = $ENV:COMPUTERNAME,
        [pscredential]$Credential,
        [switch]$AsJob,
        [switch]$Passthru
    )
    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)"
        #create a copy of original psboundparameters that can be used later
        $original = ($PSBoundParameters -as [hashtable]).clone()
    } #begin

    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Getting VMGroup $Name on $Computername"
        #remove these from psboundparameters
        "asjob", "passthru", "whatif", "confirm" | foreach-object {
            if ($PSBoundParameters.ContainsKey($_)) {
                [void]$PSBoundParameters.remove($_)
            }
        }

        Try {
            $groups = Get-VMGroup @PSBoundParameters -ErrorAction stop
        }
        Catch {
            throw $_
        }
        if ($Groups) {
            Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Found $($groups.count) matching group(s)"
            foreach ($group in $groups) {
                if ($group.grouptype -eq 'ManagementCollectionType') {
                    $members = $group.VMGroupMembers.VMMembers
                }
                else {
                    $members = $group.VMMembers
                }

                $members.where( {$_.state -ne 'running'}) | ForEach-Object {
                    Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Starting $($_.name)"
                    $original.Name = $_.Name
                    Start-VM @original
                }
            }
        }
        else {
            Write-Warning "No matching VM Groups found."
        }
    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)"

    } #end

} #close Start-VMGroup

Function Stop-VMGroup {
    [cmdletbinding(SupportsShouldProcess)]
    [Outputtype("None", "Microsoft.HyperV.PowerShell.VirtualMachine", "Microsoft.HyperV.PowerShell.Commands.VmJob")]
    Param(
        [Parameter(Position = 0, ValueFromPipeline)]
        [string]$Name,
        [ValidateNotNullorEmpty()]
        [string]$Computername = $ENV:COMPUTERNAME,
        [pscredential]$Credential,
        [switch]$Force,
        [switch]$Save,
        [switch]$TurnOff,
        [switch]$AsJob,
        [switch]$Passthru
    )
    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)"
        #create a copy of original psboundparameters that can be used later
        $original = ($PSBoundParameters -as [hashtable]).clone()

    } #begin

    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Getting VMGroup $Name on $Computername"

        #remove these from psboundparameters
        "force", "save", "asjob","turnoff", "passthru", "whatif", "confirm" | foreach-object {
            if ($PSBoundParameters.ContainsKey($_)) {
                Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] removing boundparameter $_"
                [void]$PSBoundParameters.remove($_)
            }
        }
        Try {
            $groups = Get-VMGroup @PSBoundParameters -ErrorAction stop
        }
        Catch {
            throw $_
        }
        if ($Groups) {
            Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Found $($groups.count) matching group(s)"
            foreach ($group in $groups) {
                if ($group.grouptype -eq 'ManagementCollectionType') {
                    $members = $group.VMGroupMembers.VMMembers
                }
                else {
                    $members = $group.VMMembers
                }

                $members.where( {$_.state -eq 'running'}) | ForEach-Object {
                    $original.Name = $_.Name
                    Stop-VM @original
                }
            }
        }
        else {
            Write-Warning "No matching VM Groups found."
        }
    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)"

    } #end

} #close Stop-VMGroup

Function Find-VMGroup {

    [CmdletBinding(DefaultParameterSetName = 'Name')]
    [Outputtype("Microsoft.HyperV.PowerShell.VMGroup")]
    Param(

        [Parameter(ParameterSetName = 'Id')]
        [Parameter(ParameterSetName = 'Name')]
        [ValidateNotNullOrEmpty()]
        [CimSession[]]$CimSession,

        [Parameter(ParameterSetName = 'Name')]
        [Parameter(ParameterSetName = 'Id')]
        [ValidateNotNullOrEmpty()]
        [string[]]$ComputerName,

        [Parameter(ParameterSetName = 'Name')]
        [Parameter(ParameterSetName = 'Id')]
        [ValidateNotNullOrEmpty()]
        [pscredential[]]$Credential,

        [Parameter(ParameterSetName = 'Name', Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string[]]$Name,

        [Parameter(ParameterSetName = 'Id', Position = 0)]
        [ValidateNotNullOrEmpty()]
        [guid]$Id,

        [ValidateSet("VMCollectionType", "ManagementCollectionType")]
        [string]$GroupType
    )

    Begin {

        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($MyInvocation.Mycommand)"
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Using parameter set $($PSCmdlet.ParameterSetName)"

        try {
            $outBuffer = $null
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) {
                $PSBoundParameters['OutBuffer'] = 1
            }
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Hyper-V\Get-VMGroup', [System.Management.Automation.CommandTypes]::Cmdlet)
            if ($GroupType) {
                [void]$PSBoundParameters.Remove("GroupType")
                $scriptCmd = { & $wrappedCmd @PSBoundParameters | Where-Object {$_.Grouptype -eq $GroupType} }
            }
            else {
                $scriptCmd = {& $wrappedCmd @PSBoundParameters }
            }
            $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
            $steppablePipeline.Begin($PSCmdlet)
        }
        catch {
            throw
        }

    } #begin

    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Searching for VM group(s)"
        try {
            $steppablePipeline.Process($_)
        }
        catch {
            throw
        }

    } #process

    End {

        Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($MyInvocation.Mycommand)"

        try {
            $steppablePipeline.End()
        }
        catch {
            throw
        }

    } #end

} #end function Find-VMGroup

Function Get-VMIPAddress {
    [cmdletbinding(DefaultParameterSetName = "computer")]
    [outputtype("vmIPAddress")]

    Param (
        [Parameter(Position = 0, Mandatory,
            HelpMessage = "Enter a Hyper-V virtual machine name",
            ValueFromPipeline, ValueFromPipelinebyPropertyName)]
        [ValidateNotNullorEmpty()]
        [alias("vm")]
        [object]$Name,
        [Parameter(ValueFromPipelinebyPropertyName, ParameterSetName = "computer")]
        [ValidateNotNullorEmpty()]
        [string]$Computername = $env:COMPUTERNAME,
        [Parameter(ParameterSetName = "computer")]
        [PSCredential]$Credential,
        [Parameter(ParameterSetName = "session")]
        [Microsoft.Management.Infrastructure.CimSession]$Cimsession
    )

    Begin {
        Write-Verbose -Message "Starting $($MyInvocation.Mycommand)"
    } #begin

    Process {
        if ($name -is [string]) {
            Write-Verbose -Message "Getting virtual machine(s)."
            $vms = Get-VM @PSBoundParameters
        }
        else {
            $vms = $name
        }
        #otherwise we'll assume $Name is a virtual machine object
        foreach ($vm in $vms) {
            Write-Verbose -Message "Getting network information from $($vm.name)"
            $data = $vm | Get-VMNetworkAdapter -PipelineVariable pv |
                Select-Object -ExpandProperty IPAddresses -first 1 |
                Select-Object -first 1 -Property @{Name = "IP"; Expression = {$_}},
            @{Name = "Switch"; Expression = {$pv.SwitchName}},
            @{Name = "MAC"; Expression = {$pv.macaddress}}

            [pscustomobject]@{
                PSTypename   = "vmIPAddress"
                Name         = $vm.name
                IPAddress    = $data.IP
                MACAddress   = $data.mac
                Switch       = $data.Switch
                Computername = $vm.computername
            }
        } #foreach
    } #process

    End {
        Write-Verbose -Message "Ending $($MyInvocation.Mycommand)"
    } #end

} #end Get-VMIPAddress

Function Open-VMRemoteDesktop {
    [cmdletbinding()]
    [Outputtype("None")]

    Param(
        [Parameter(Position = 0, Mandatory,
            HelpMessage = "Enter a Hyper-V virtual machine name",
            ValueFromPipeline, ValueFromPipelinebyPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ValueFromPipelinebyPropertyName)]
        [ValidateNotNullorEmpty()]
        [string]$Computername = $env:COMPUTERNAME,
        [switch]$Admin,
        [switch]$FullScreen
    )

    Begin {
        Write-Verbose -Message "Starting $($MyInvocation.Mycommand)"
    } #begin
    Process {
        Write-Verbose "Getting IP address for $Name on $Computername"
        $IPAddress = (Get-VMIPAddress -Name $Name -ComputerName $Computername).IPAddress
        #use the first address found for the virtual machine if more than one
        if ($IPAddress -is [array]) {
            $IPAddress = $IPAddress[0]
        }
        #define a command string which will eventually be turned into a scriptblock
        $cmd = "mstsc -v $IPAddress"
        if ($admin) {
            Write-Verbose "Adding /Admin"
            $cmd += " /Admin"
        }
        if ($FullScreen) {
            Write-Verbose "Adding /f for full screen"
            $cmd += " /f"
        }
        Write-Verbose -Message ("Connecting to {0} [{1}]" -f $Name, $IPAddress)
        #create a scriptblock from the $cmd string
        $sb = [scriptblock]::Create($cmd)
        Invoke-Command -ScriptBlock $sb
    } #process
    End {
        Write-Verbose -Message "Ending $($MyInvocation.Mycommand)"
    } #end

} #end Open-VMRemoteDesktop

Function Open-VMConnect {
    [cmdletbinding()]
    [outputtype("None")]

    Param(
        [Parameter(Position = 0, Mandatory,
            HelpMessage = "Enter a Hyper-V virtual machine name",
            ValueFromPipeline, ValueFromPipelinebyPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateNotNullorEmpty()]
        [string]$Computername = $env:computername
    )

    Begin {
        Write-Verbose -Message "Starting $($MyInvocation.Mycommand)"
    } #begin
    Process {

        $cmdstring = "vmconnect $computername '$name'"

        $cmd = [scriptblock]::Create($cmdstring)

        Write-Verbose -Message "Connecting to $name on $computername"
        Invoke-Command -ScriptBlock $cmd
    } #process
    End {
        Write-Verbose -Message "Ending $($MyInvocation.Mycommand)"
    } #end

} #end Open-VMConnect

Function Get-VHDSummary {
    Param()

    #get all virtual machines
    $vms = Get-VM

    foreach ($vm in $vms) {
        Write-Host "Getting drive info from $($vm.name)" -foregroundcolor Cyan
        #get the hard drives foreach virtual machine
        $vm.HardDrives | ForEach-Object {
            #a VM might have multiple drives so for each one get the VHD
            $vhd = Get-VHD -path $_.path

            <#
       $_ is the hard drive object so select a few properties and
       include properties from the VHD
      #>

            $_ | Select-Object -property VMName, Path,
            @{Name = "Type"; Expression = {$vhd.VhdType}},
            @{Name = "Format"; Expression = {$vhd.VhdFormat}},
            @{Name = "SizeGB"; Expression = {[math]::Round(($vhd.Size) / 1GB, 2)}},
            @{Name = "FileSizeGB"; Expression = {[math]::Round(($vhd.FileSize) / 1GB, 2)}}
        } #foreach
    } #foreach vm

} #end Get-VHDSummary

Function Get-VMState {
    <#
    this is a proxy function to the Hyper-V Get-VM that
    allows you to retrieve virtual machines by their state,
    i.e. stopped or running. The default is Running
 
    #>

    [CmdletBinding(DefaultParameterSetName = 'Name')]
    [outputtype("Microsoft.HyperV.PowerShell.VirtualMachine")]
    Param(

        [Parameter(ParameterSetName = 'Name', Position = 0, ValueFromPipeline)]
        [Alias('VMName')]
        [ValidateNotNullOrEmpty()]
        [string[]]$Name,

        [Parameter(ParameterSetName = 'Name')]
        [Parameter(ParameterSetName = 'Id')]
        [ValidateNotNullOrEmpty()]
        [CimSession[]]$CimSession,

        [Parameter(ParameterSetName = 'Name')]
        [Parameter(ParameterSetName = 'Id')]
        [ValidateNotNullOrEmpty()]
        [string[]]$ComputerName,

        [Parameter(ParameterSetName = 'Name')]
        [Parameter(ParameterSetName = 'Id')]
        [ValidateNotNullOrEmpty()]
        [pscredential[]]$Credential,

        [Parameter(ParameterSetName = 'Id', Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateNotNull()]
        [System.Nullable[guid]]$Id,

        [Parameter(ParameterSetName = 'ClusterObject', Mandatory, Position = 0, ValueFromPipeline )]
        [ValidateNotNullOrEmpty()]
        [PSTypeName('Microsoft.FailoverClusters.PowerShell.ClusterObject')]
        [psobject]$ClusterObject,

        [Microsoft.HyperV.PowerShell.VMState]$State = 'Running'
    )

    begin {
        try {
            $outBuffer = $null
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) {
                $PSBoundParameters['OutBuffer'] = 1
            }
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Hyper-V\Get-VM', [System.Management.Automation.CommandTypes]::Cmdlet)
            $PSBoundParameters.Remove('State') | Out-Null
            $scriptCmd = {& $wrappedCmd @PSBoundParameters | Where-Object state -eq $state }
            $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
            $steppablePipeline.Begin($PSCmdlet)
        }
        catch {
            throw
        }
    }

    process {
        try {
            $steppablePipeline.Process($_)
        }
        catch {
            throw
        }
    }

    end {
        try {
            $steppablePipeline.End()
        }
        catch {
            throw
        }
    }

} #end Get-VMState

Function Get-VMMemorySummary {

    [CmdletBinding(DefaultParameterSetName = 'NamebyComputer')]
    [Outputtype("vmMemorySummary")]

    Param(

        [Parameter(ParameterSetName = 'NamebyComputer', Position = 0, ValueFromPipeline )]
        [Parameter(ParameterSetName = 'NamebySession')]
        [Alias('VMName')]
        [ValidateNotNullOrEmpty()]
        [string[]]$Name,

        [Parameter(ParameterSetName = "VM", ValueFromPipeline)]
        [Microsoft.HyperV.PowerShell.VirtualMachine[]]$VM,

        [Parameter(ParameterSetName = 'NamebySession')]
        [Parameter(ParameterSetName = 'IdbySession')]
        [ValidateNotNullOrEmpty()]
        [CimSession[]]$CimSession,

        [Parameter(ParameterSetName = 'NamebyComputer')]
        [Parameter(ParameterSetName = 'IdbyComputer')]
        [ValidateNotNullOrEmpty()]
        [string[]]$ComputerName,

        [Parameter(ParameterSetName = 'NamebyComputer')]
        [Parameter(ParameterSetName = 'IdbyComputer')]
        [ValidateNotNullOrEmpty()]
        [pscredential[]]$Credential,

        [Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'IdbySession')]
        [Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'IdbyComputer')]
        [ValidateNotNull()]
        [System.Nullable[guid]]$Id

    )

    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)"

    } #begin

    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Using parameter set $($pscmdlet.ParameterSetName)"
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Retrieving virtual machines with these parameters"
        $psboundparameters | Out-String | Write-Verbose

        if ($pscmdlet.ParameterSetName -eq 'VM') {
            $vms = $VM
        }
        else {
            Try {
                $vms = Get-VM @psboundparameters
            }
            Catch {
                Throw $_
            }
        }

        #get memory values
        foreach ($vm in $vms) {

            $data = $vm | Get-VMMemory

            #all values are in MB
            [pscustomobject]@{
                PSTypeName   = "vmMemorySummary"
                Name         = $vm.Name
                Dynamic      = $vm.DynamicMemoryEnabled
                Assigned     = $vm.MemoryAssigned / 1MB
                Demand       = $vm.MemoryDemand / 1MB
                Startup      = $vm.MemoryStartup / 1MB
                Minimum      = $vm.MemoryMinimum / 1MB
                Maximum      = $vm.MemoryMaximum / 1MB
                Buffer       = $data.buffer
                Priority     = $data.priority
                Computername = $vm.ComputerName
                Date         = (Get-Date)
            } #custom object
        } #foreach VM

    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)"

    } #end
} #end Get-VMMemorySummary