Skip to main content

Have PowerShell Verbally Provide Information

Let’s face it.  Sometimes scripts take a while to run.   Often I have a lower end client sitting in the back of my office specifically for executing schedule scripts or scripts that will run for a long period of time.  Normally I have the scripts email me once they have completed and I simply run Receive-job to collect the data.
I developed this cmdlet as a fun way of demonstrating passing input via the pipeline into a cmdlet.  It gives you the opportunity to have PowerShell talk to you.  Maybe to alert you that a threshold has been exceeded or to let you know that a workflow has completed.  In any case, have some fun with.  Remember its intent was to act as a demonstration of parameter passing.  Look carefully and you will see examples of passing parameters ByValue and ByPropertyName.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

Function Out-Voice
{
[cmdletBinding()]
Param
(
    [parameter(ValueFromPipeline=$True,
               ValueFromPipelineByPropertyName=$True)]
               [String]$VoiceMessage,
    [parameter(ValueFromPipelineByPropertyName=$True)]
    [ValidateSet("US_Male","UK_Female","US_Female")]          
               [String]$VoiceType,
               [Switch]$PassThru,
    [parameter(ValueFromPipeline=$True)]
               [PSObject]$InputObject         
)
    BEGIN
    {
        # When the cmdlet starts, create a new
        # SAPI voice object.
        $voice = New-Object -com SAPI.SpVoice
    }
   
    PROCESS
    {
        # If an array of messages is passed, this will allow
        # for each message to be read.
        ForEach ($M in $VoiceMessage)
        {
            # If the client is Windows 8, then allow for different voices.
            If ((Get-CimInstance -ClassName Win32_Operatingsystem).Name -Like "*Windows 8*")
            {             
                # Get a list of all voices.
                $Voice.GetVoices() | Out-Null
                $voices = $Voice.GetVoices();
                $V = @()
                ForEach ($Item in $Voices)
                {
                    $V += $Item
                }                  
                   
                # Set the voice to use using the $VoiceType parameter.
                # The defualt voice will be used otherwise.
                Switch ($VoiceType)
                {
                    "US_Male" {$Voice.Voice = $V[0]}
                    "UK_Female" {$Voice.Voice = $V[1]}
                    "US_Female" {$Voice.Voice = $V[2]}
                }
            } # End: IF Statment.
            # Speak the message.
            $voice.Speak($M) | Out-Null
        }
    } # End: ForEach ($M in $VoiceMessage)
   
    END
    {
        If ($PassThru)
        {
            Write-Output $InputObject
        }
    } #End: PROCESS

<#
.SYNOPSIS
Reads a message to the user.

.DESCRIPTION
Uses the default voice of the client to read a message
to the user.

.PARAMETER Message
The string of text to read.

.PARAMETER VoiceType
Allows for the default choice to be changed using the
default voices installed on Windows 8. Acceptable values are:
US_Male
UK_Female
US_Female

.PARAMETER PassThru
Passes the piped in object to the pipeline.

.EXAMPLE
Out-Voice "Script Complete"

Reads back to the user "Script Complete"

.EXAMPLE
$CustomObject | Out-Voice -PassThru

If the object has a property called "VoiceMessage" and is of
data type [STRING], then the string is read.  If the object
contains a "VoiceType" parameter that is valid, that
voice will be used. The original object is then passed
into the pipeline.

.EXAMPLE
Get-WmiObject Win32_Product |
ForEach -process {Write-Output $_} -end{Out-Voice -VoiceMessage "Script Completed"}

Recovers the product information from WMI and the notifies the
user with the voice message "Script Completed" while also
passing the results to the pipeline.

.EXAMPLE
Start-Job -ScriptBlock {Get-WmiObject WIn32_Product} -Name GetProducts
While ((Get-job -Name GetProducts).State -ne "Completed")
{
    Start-sleep -Milliseconds 500
}
Out-Voice -VoiceMessage "Done"

Notifies the user when a background job has completed.

.NOTES
Tested on Windows 8
+-------------------------------------------------------------+
| Copyright 2013 MCTExpert, Inc. All Rights Reserved          |
| User takes full responsibility for the execution of this    |
| and all other code.                                         |
+-------------------------------------------------------------+
#>
} # End: Out-Voice



Lines 6-7 allow the parameter $VoiceMessage to pass information ByValue and ByProperty name.  This string is what Windows will read back to you.  By having ByPropertyName, this allows you to add a “VoiceMessage” property to your objects should you need a verbal message given.
Lines 9-11 allow you to choose the voice that you want to use.  There are three built in voices.  Advanced parameterization using the ValidateSet attribute allows you to provide TAB completion of this parameter.
The PassThru and InputObject parameters allows the original object piped to this cmdlet to continue in the pipeline.
Lines 16-21 executes when the pipeline first starts.  The Begin block will create an object of Windows Text-To-speech capabilities into PowerShell and allow you to use it.
Lines 23-26 runs for each object passed to this cmdlet. Should multiple lines of messages be passed to the cmdlet, a ForEach loop will handle them.
Line 30 test to make sure that this code is running on Windows 8.  The code was only tested on Windows 8.  To test on other platforms, change *Windows 8* to *.
Lines 32-39 collect the different voice objects.
Lines 43-48 set the voice based on the VoiceType parameter.  US_Male is the default.
Line 51 executes the Speak method and reads the text to the user.
Lines 55-61 is the END block.  it will only execute once after all elements in the upstream pipeline have been exhausted.  If the PassThru parameter has been specified, then the original object is passed through the pipeline.

Comments

Popular posts from this blog

How to list all the AD LDS instances on a server

AD LDS allows you to provide directory services to applications that are free of the confines of Active Directory.  To list all the AD LDS instances on a server, follow this procedure: Log into the server in question Open a command prompt. Type dsdbutil and press Enter Type List Instances and press Enter . You will receive a list of the instance name, both the LDAP and SSL port numbers, the location of the database, and its status.

How to run GPResult on a remote client with PowerShell

In the past, to run the GPResult command, you would need to either physically visit this client, have the user do it, or use and RDP connection.  In all cases, this will disrupt the user.  First, you need PowerShell remoting enabled on the target machine.  You can do this via Group Policy . Open PowerShell and type this command. Invoke-Command –ScriptBlock {GPResult /r} –ComputerName <ComputerName> Replace <ComputerName> with the name of the target.  Remember, the target needs to be online and accessible to you.

Error icon when creating a GPO Preference drive map

You may not have an error at all.  Take a look at the drive mapping below. The red triangle is what threw us off.  It is not an error.  It is simply a color representation of the Replace option of the Action field in the properties of the drive mappings. Create action This give you a green triangle. The Create action creates a new mapped drive for users. Replace Action The Replace action gives you a red triangle.  This action will delete and recreate mapped drives for users. The net result of the Replace action is to overwrite all existing settings associated with the mapped drive. If the drive mapping does not exist, then the Replace action creates a new drive mapping. Update Action The Update action will have a yellow triangle. Update will modify settings of an existing mapped drive for users. This action differs from Replace in that it only updates settings defined within the preference item. All other settings remain as configured on the ma...