CMAWS.psm1

#Requires -version 3.0
#Requires -Module AwsPowerShell
Function Convert-TextToSpeech        {
<#
.Synopsis
    Converts text files into audio files.
.DESCRIPTION
    Parses the contents of multiple text files and outputs each file to an audio file by using Amazon's Polly service (https://aws.amazon.com/polly/)
 
.NOTES
    Name: Convert-TextToSpeech
    Author: Chad Miles
    DateUpdated: 2017-05-02
    Version: 1.2.0
 
.EXAMPLE
   Convert-TextToSpeech input.txt -Voice Joanna
 
   This will output input.mp3 using Amy voice
.EXAMPLE
   Convert-TextToSpeech *.txt -Voice Amy -OutputFormat ogg
    
   This will convert all the .txt files to .ogg files in Joanna voice.
#>

    [CmdletBinding()]
    Param (
        #The Input file(s), wilcards and just pointing to a folder itself work fine, just know that it will suck in what you ever you give it.
        [Parameter(Mandatory=$true)]
        [string[]]  $InputFiles, 
        #Format of the output, mp3, ogg or pcm
        [string]    $OutputFormat = "mp3",
        #The voice used, default is Amy (English - UK), for all voices please run Get-POLVoice | select -ExpandProperty Id
        [string]    $Voice = "Amy"
    )
    $ErrorActionPreference = "Stop"
    Write-Verbose "Validating Voice"
    $VoiceIds   = (Get-POLVoice).Id.Value
    if ($VoiceIds -notcontains $Voice) {
        Write-Error "$Voice is not a valid voice, Valid Voices are $VoiceIds"
    }
    Write-Verbose "Processing Files"
    $PreText        = '<speak>'
    $PostText       = '<break time="350ms"/></speak>'
    $PollyLimit     = 1000
    $MaxTextLength  = $PollyLimit - ($PreText.Length + $PostText.Length)
    $InputFileNames = (dir $InputFiles).FullName
    Foreach ($InputFile in $InputFileNames)
    {
        $Text           = Get-Content $InputFile
        $LongLines      = $false
        Foreach ($Line in $Text){
            $FileName       = $InputFile.Name
            $LineLength     = $PreText.Length + $Line.Length + $PostText.Length
            If ($LineLength -ge $PollyLimit){
                Write "$FileName line $LineNo is $LineLength long"
                $LongLines  = $true
                }   
        }
        If ($LongLines) {
            Write-Warning "$FileName was skipped as it has lines that are longer than $MaxTextLength which is the longest we can submit to Polly excluding the SSML tags."
        } else {
            $DirectoryName  = (dir $InputFile).Directory
            $BaseName       = (dir $InputFile).BaseName
            $OutputFile     = "$DirectoryName\$BaseName.$OutputFormat"
            Write-Host       "Converting $InputFile to $OutputFile"
            $OutputStream   = New-Object System.IO.FileStream $OutputFile, Create
            Foreach ($Line in $Text)
            {
                (Get-POLSpeech -Text $PreText$Line$PostText -TextType ssml -VoiceId $Voice -OutputFormat $OutputFormat).AudioStream.CopyTo($OutputStream)
            }
            (Get-POLSpeech -Text '<speak><break time="2s"/></speak>' -TextType ssml -VoiceId $Voice -OutputFormat $OutputFormat).AudioStream.CopyTo($OutputStream)
            $OutputStream.Close()
        }
    }
}
Function New-CMEC2Instance           {
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param (
        [Parameter(Mandatory=$true)]
        [string] $InstanceType,
        [Parameter(Mandatory=$true)]
        [string] $Region,
        [string] $Name,
        [string] $DomainName,
        [int]    $Count      = 1,
        # WINDOWS or LINUX, Default is Windows
        [string] $OS         = "Windows",
        # Version of Windows e.g. 2012R2 or 2016. Default is 2012R2
        [string] $OSVersion  = "2012R2",
        # BASE or SQL_EDITION_YEAR, Must be uppercase
        [string] $AmiEdition = "BASE",
        # Instance Profile (with IAM Role) to attach to new Instance
        [string] $InstanceProfile,
        # Path to User data file , using Your My Documents folder as a root
        [string] $UserData,
        # What Percrentage to add to the lowest Spot Price to ensure instance's longevity
        [int]    $MarkUp     = 1,
        [Parameter(ValueFromPipeline       =$true,
            ValueFromPipelineByPropertyName=$true)]
        [string] $ImageID,
        [string] $SecurityGroupName,
        [switch] $SpotInstance,
        [string] $KeyName,
        [string] $VpcId,
        # Last letter of the Availability Zone like a, b, c, d or e
        [string] $AZSuffix    = "a"    
    ) 
    If ($InstanceType -like "t*" -and $SpotInstance) {
        Write-Warning "Spot Instances not available for T1 and T2 instance types, switching to on demand."
        $SpotInstance = $false
    }
    $AvailabilityZone      = $Region+$AZSuffix
    $ErrorActionPreference = "Stop"
    $OS                    = $OS.ToUpper()
    $AmiEdition            = $AmiEdition.ToUpper()
    $OSVersion             = $OSVersion.ToUpper()
    $Region                = $Region.ToLower()
    $InstanceType          = $InstanceType.ToLower()
    Write-Verbose          "Geting On Demand Instance Pricing"
    $OndemandPrice         = Get-EC2WindowsOndemandPrice -InstanceType $InstanceType -Region $Region
    If ($SpotInstance) {
        Write-Verbose      "Getting Current Spot Price of Instance Type and adding mark up."
        $OSLower           = $OS.Substring(0,1).ToUpper()+$OS.Substring(1).ToLower()
        $CurrentSpotPrice  = ((Get-EC2SpotPriceHistory -Region $Region -StartTime (Get-Date) -InstanceType $InstanceType -ProductDescription $OSLower).Price | Measure-Object -Minimum).Minimum
        $BidPrice          = $CurrentSpotPrice + ($CurrentSpotPrice * ($MarkUp/100))
    
        If ($OndemandPrice -gt $BidPrice) {
            $Savings       = [math]::Round((($OndemandPrice - $BidPrice) / $OnDemandPrice)*100)
            Write-Verbose     "Requesting $Count $InstanceType instance(s) at $ $BidPrice/hour ($Savings% Savings)" 
            
        } else { 
            Write-Error    "OnDemand Pricing ($ $OndemandPrice) is better than Spot Price ($ $BidPrice)"}
    }
    $ImageName     = $OS+"_"+$OSVersion+"_"+$AMIEdition   
    if (!$ImageID)    {
        Write-Verbose       "Getting Current AMI for Selected OS"
        $ImageID           = (Get-EC2ImageByName -Region $Region -Name $ImageName -Verbose).ImageId
    } 
    if (!$Keyname) {
        Write-Verbose       "Getting first KeyPair for Region"
        $KeyName           = (Get-EC2KeyPair -Region $Region)[0].KeyName
    }
    If ($UserData) {
        $UserData64        = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($UserData))
    }
    If (!$VPCid){
        Write-Verbose       "Getting default VPC"
        $VPCId             = (Get-EC2Vpc | Where {$_.IsDefault -eq $true}).VpcId
    }
    If ($SecurityGroupName) {
        Write-Verbose       "Getting Security Group for name $SecurityGroupName"
        $SecurityGroupId   = (Get-EC2SecurityGroup -Region $Region | where {$_.GroupName -eq $SecurityGroupName -and $_.VpcId -eq $VpcId})[0].GroupId
        If (!$SecurityGroupName) {Write-Error "Security Group with $SecurityGroupName cannot be found"}
    } else {
        Write-Verbose       "Getting Security Group for VPC $VpcId"
        $SecurityGroupId   = (Get-EC2SecurityGroup -Region $Region | where {$_.GroupName -eq "default" -and $_.VpcId -eq $VPCId})[0].GroupId
    
    If (!$SpotInstance){
        Write-Verbose       "Getting Subnet for name $AvailabilityZone"
        $SubNetId          = (Get-EC2Subnet -Region $Region | where {$_.AvailabilityZone -eq $AvailabilityZone -and $_.VpcId -eq $VPCid})[0].SubnetId
    }
    
    }
    If ($SpotInstance) {
        $Params            = @{
            Region             = $Region
            InstanceCount      = $Count
            SpotPrice          = $BidPrice
            Type               = "one-time"
            IamInstanceProfile_Name           = $InstanceProfile
            LaunchSpecification_ImageId       = $ImageID
            LaunchSpecification_InstanceType  = $InstanceType
            LaunchSpecification_KeyName       = $KeyName
            LaunchSpecification_UserData      = $UserData64
        }

        $SpotRequest       = Request-EC2SpotInstance @Params
        Write-Host          "Waiting for Spot Fufillment"
        $InstanceID        = $false
        While (!$InstanceID){
            Start-Sleep   1
            $InstanceID    = (Get-EC2SpotInstanceRequest $SpotRequest.SpotInstanceRequestId).Instanceid
        }
    } else {
        $Params            = @{
            Region               = $Region
            MinCount             = $Count
            MaxCount             = $Count
            InstanceType         = $InstanceType
            SecurityGroupId      = $SecurityGroupId
            InstanceProfile_Name = $InstanceProfile
            ImageId              = $ImageID
            SubnetId             = $SubNetId
            KeyName              = $KeyName
            UserData             = $UserData64
        }
        $NewInstance       = New-EC2Instance @Params   
        $InstanceID        = ($NewInstance.Instances).InstanceID
        $Savings           = 0
    }   

    $InstanceInfo          = Get-EC2Instance -InstanceId $InstanceID -Region $Region
    $PublicDNSName         = $InstanceInfo.RunningInstance.PublicDNSName
        
    if ($Name) {
        $Tag          = New-Object Amazon.EC2.Model.Tag
        $Tag.Key      = "Name"
        $Tag.Value    = "$Name"
        Write-Verbose "Applying Name Tag to instance"
        New-EC2Tag -Resource $InstanceID -Tag $Tag -Region $Region
    }

    if ($DomainName -and $Name) {
        $HostName = $Name+"."+$DomainName
        Write-Verbose "Creating DNS entry $HostName"
        Set-R53Record -Domain $DomainName -Type CNAME -Name $Name -Value $PublicDNSName -TTL 30 | Out-Null
        
    } else { $HostName = $PublicDNSName}
    $OutputProperties = @{
        InstanceID            = $InstanceID
        Name                  = $Name
        Hostname              = $HostName
        InstanceType          = $InstanceType
        BidPrice              = $BidPrice
        OnDemandPrice         = $OnDemandPrice
        Savings               = "$Savings %"
        ImageID               = $ImageID
        KeyName               = $KeyName
    }
    $OutputObject   = New-Object -TypeName PSObject -Property $OutputProperties
    Write-Output      $OutputObject
}
Function New-CMPassword              {
    <#
    .Synopsis
       Generates random passwords
    .DESCRIPTION
       Generates Random passwords and outputs as text and to Clipboard or just to clipboard
    .EXAMPLE
       PS> New-CMPassword -Easy
       Kobi2858
    .EXAMPLE
       PS> New-CMPassword
       N42v9AjCWF1J
    .EXAMPLE
       PS> New-CMPassword -ClipOnly
       PS>
       Password is only put in clipboard and not displayed on screen.
    .EXAMPLE
       PS> $pass = New-CMPassword
       PS> Set-ADAccountPassword DOMAIN\USERNAME -NewPassword (ConvertTo-SecureString ($pass) -AsPlainText -Force)
       PS> Write-Output "Account password set to $pass"
 
       This will set an AD account's password to a randomly generated password and copy that password to the clipboard
    #>

    [CmdletBinding()]
    [Alias('np')]
    [OutputType([String])]
    Param
    (
        #Creates easy readable and rememberable password like Office 365's password generator
        [Switch]
        $Easy, 
        [Switch]
        #Enables the use of special characters, like !^&*(%$#)-=_+
        $Specials,
        [Switch]
        #Doesn't output the password as text but rather only copies it to Clipboard
        $ClipOnly,
        #Specifies the length of the password. This parameter is ignored when you use the -Easy switch
        [Parameter(Position=0)]
        [int]$Length = 12,
        #Specifies the ammount of passwords to generate, only the last one will be left in the clipboard
        [int]$Count = 1
    )
    for ($c = 0 ; $c -lt $Count ; $c++){
        if ($easy -Eq $true) 
        {
            $digits = 48..57
            $UpperConsonants = 66..68 + 70..72 + 74..78 + 80..84 + 86..90
            $LowerConsonants = 98..100 + 102..104 + 106..110 + 112..116 + 118..122
            $LowerVowels = 97, 101, 105, 111, 117

            $first = [char](get-random -count 1 -InputObject $UpperConsonants)
            $second = [char](get-random -count 1 -InputObject $LowerVowels)
            $third = [char](get-random -count 1 -InputObject $LowerConsonants)
            $fourth = [char](get-random -count 1 -InputObject $LowerVowels)
            $numbers = $null
            for ($i=0 ; $i -lt 4; $i++)
            {
                $numbers += [char](get-random -count 1 -InputObject $digits)
            }
            $password = ($first + $second + $third + $fourth + $numbers)
        }
        Else
        {
            $digits = 48..57
            $letters = 65..90 + 97..122
            $specialchar = 33..47 + 58..64 + 91..96 + 123..126
            $password = $null
            for ($i = 0 ; $i -lt $Length ; $i++)
            {
                If ($Specials -eq $true) {$password += [char](get-random -count 1 -InputObject ($digits + $letters + $specialchar))}
                Else {$password += [char](get-random -count 1 -InputObject ($digits + $letters))}
            }
        }
        $password | Clip
        If($ClipOnly -ne $true) {$password}
    }
}
Function New-CMEasyPassword          {
    <#
    .Synopsis
       See New-CMPassword Help
 
       Alais for executing New-CMPassword -Easy -Count 1
    #>

    Param(
    $Count = 1,
    [Switch]
    $ClipOnly)
    if ($ClipOnly -eq $true) {New-CMPassword -Easy -Count $Count -ClipOnly}
    else {New-CMPassword -Easy -Count $Count}
}
Function Enter-AWSPSSession          {
    [Cmdletbinding()]
    Param(
        $DomainName = "chadmile.myinstance.com",
        $Pem,
        $Port = 22,
        $Instance
    )
    $RunningInstance = (Get-EC2Instance -InstanceId $Instance).RunningInstance
    $InstanceTag     = $RunningInstance.Tags | Where-Object {$_.Key -eq "Name"} | Select -ExpandProperty Value
    $GetPassword     = Get-EC2PasswordData -InstanceId $Instance -PemFile $Pem
    $Password        = ConvertTo-SecureString $GetPassword -AsPlainText -Force
    $Credential      = New-Object System.Management.Automation.PSCredential (".\Administrator", $Password)
    $SessionOptions  = New-PSSessionOption -SkipCACheck -SkipCNCheck
    $ComputerName    = $InstanceTag+"."+$DomainName
    Enter-PSSession -ComputerName $ComputerName -UseSSL -Port 22 -Credential $Credential -SessionOption $SessionOptions
}
Function Get-EC2WindowsOndemandPrice {
    Param(
        [Parameter(Mandatory=$true)]
        $InstanceType,
        [Parameter(Mandatory=$true)]
        $Region)
    $OnDemandPricing    = Invoke-RestMethod -uri http://a0.awsstatic.com/pricing/1/ec2/mswin-od.min.js
    $CurrentYear        = (Get-Date).Year
    $IntroText          = '/*
* This file is intended for use only on aws.amazon.com. We do not guarantee its availability or accuracy.
*
* Copyright '
+$CurrentYear+' Amazon.com, Inc. or its affiliates. All rights reserved.
*/
callback('
 | Out-String
    $OnDemandPricing    = $OnDemandPricing.TrimStart($IntroText)
    $OnDemandPricing    = $OnDemandPricing.TrimEnd(');')
    $PricingObject      = ($OnDemandPricing | ConvertFrom-Json).config.regions
    $RegionPrices       = $PricingObject | where {$_.region -eq $Region}
    $AllInstances       = $RegionPrices.instancetypes.sizes
    $InstanceEntry      = $AllInstances| where {$_.size -eq $InstanceType}
    $Price              = $InstanceEntry.valuecolumns.prices.usd
    Write-Output $Price
}
Function Invoke-AWSCommand           {
    [Cmdletbinding()]
    Param(
        $ScriptBlock,
        $DomainName = "chadmile.myinstance.com",
        $Pem,
        $Port = 22,
        $Instance
    )
    $RunningInstance = (Get-EC2Instance -InstanceId $Instance).RunningInstance
    $InstanceTag = $RunningInstance.Tags | Where-Object {$_.Key -eq "Name"} | Select -ExpandProperty Value
    $GetPassword = Get-EC2PasswordData -InstanceId $Instance -PemFile $Pem
    $Password = ConvertTo-SecureString $GetPassword -AsPlainText -Force
    $Credential = New-Object System.Management.Automation.PSCredential (".\Administrator", $Password)
    $SessionOptions = New-PSSessionOption -SkipCACheck -SkipCNCheck
    $ComputerName = $InstanceTag+"."+$DomainName
    Invoke-Command -ComputerName $ComputerName -UseSSL -Port 22 -Credential $Credential -SessionOption $SessionOptions $ScriptBlock
}
Function Get-CMEC2Instances          {
    [Cmdletbinding()]
    Param (
        [string[]]$Region
        )
    $ErrorActionPreference = "Stop"
    $AllRegions    = (Get-AWSRegion).Region
    If (!$Region) {
        $Regions = $AllRegions
        Write-Warning "Getting instances for all regions"
    }
    Else {$Regions = $Region}
    $InstancesList = New-Object -TypeName System.Collections.ArrayList
    Foreach ($Reg in $Regions)
    {
        If ($AllRegions -notcontains $Reg){Write-Error "$Region is not a valid AWS Region, Valid regions are $AllRegions"}
        $Instances = (Get-EC2Instance -Region $Reg).RunningInstance 
        Foreach ($Instance in $Instances)
        {  
            $Properties    = @{
                Name            = $Instance.Tags | Where-Object {$_.Key -eq "Name"} | Select -ExpandProperty Value
                State           = $Instance.State.Name
                InstanceType    = $Instance.InstanceType
                InstanceID      = $Instance.InstanceID
                Region          = $Reg
                LaunchTime      = $Instance.LaunchTime
                PublicIpAddress = $Instance.PublicIpAddress
            }
            $InstanceObject = New-Object PSObject -Property $Properties
            $InstancesList.Add($InstanceObject) | Out-Null
        }
    }
    Write-Output $InstancesList
}
Function Set-R53Record               {
<#
.SYNOPSIS
    Made for easier interaction with Amazon Route 53 DNS service.
.DESCRIPTION
    Run the script in CREATE/UPDATE mode in order to add or modify DNS records in Amazon Route 53.
    Requires 4 parameters - Domain name and type, name and the value of DNS record.
.NOTES
    File name : Set-R53Record.ps1
    Author : Sinisa Mikasinovic - six@mypowershell.space
    Date : 02-Jan-17
    Script created as part of a learning tutorial at mypowershell.space.
    http://mypowershell.space/index.php/2017/01/02/amazon-route-53-records/
    All expected functionality may not be there, make sure you give it a test run first.
    Feel free to update/modify. I'd be interested in seeing it improved.
    This script example is provided "AS IS", without warranties or conditions of any kind, either expressed or implied.
    By using this script, you agree that only you are responsible for any resulting damages, losses, liabilities, costs or expenses.
.LINK
    http://mypowershell.space
.EXAMPLE
    Set-R53Record -Domain mypowershell.space -Type A -Name www -Value 1.2.3.4 -TTL 300
    Create an A record to point www.mypowershell.space to IP 1.2.3.4. TTL set to 5 minutes.
.EXAMPLE
    Set-R53Record -Domain mypowershell.space -Type A -Name mail -Value 1.2.3.4 -TTL 3600 -Comment "mail entry"
    Create an A record to point mail.mypowershell.space to IP 1.2.3.4. TTL set to 60 minutes and has an optional comment.
.EXAMPLE
    Set-R53Record -Domain mypowershell.space -Type TXT -Name _amazonses -Value "G3LNeKkT8eYmQLeyAp" -Comment "confirm domain ownership"
    Create a TXT record to set _amazonses.mypowershell.space to "G3LNeKkT8eYmQLeyAp" and confirm domain ownership. Will use default TTL (300) and no comment.
.PARAMETER Domain
    Defines a domain which DNS zone is to be edited:
    1. mypowershell.space
    2. amazon.com
    3. google.com.
    4. facebook.com.
.PARAMETER Type
    Defines a type of a DNS record: A, TXT, MX, CNAME, NS...
    Most likely won't support all. If you mod the script and add functionality, let me know!
.PARAMETER Name
    Defines a name of a DNS record: www, mail, intranet, dev...
.PARAMETER Value
    Defines a value of DNS record:
    1. 192.168.0.1
    2. "ZTJGIJ4OIJS9J3560S"
    Bear in mind which record type is numerical and which textual!
.PARAMETER TTL
    Defines a TTL of DNS record. I shouldn't really need to explain this :-)
    Not mandatory, defaults to 300.
.PARAMETER Comment
    Defines an optional R53 comment.
    Not mandatory, not included if not explicitly defined.
#>

    # Entry parameters
    Param (
        [Parameter(Mandatory=$True)][String]$Domain,
        [Parameter(Mandatory=$True)][String]$Type,
        [Parameter(Mandatory=$True)][String]$Name,
        [Parameter(Mandatory=$True)][String]$Value,
        [Int]$TTL = 300,
        [String]$Comment
    )

    # Check of user entered domain without the dot, and add it
    if ($Domain.Substring($Domain.Length-1) -ne ".") {
        $DomainDot = $Domain + "."
    } else {
        $DomainDot = $Domain
    }

    # Create two objects for R53 update
    $Change = New-Object Amazon.Route53.Model.Change
    $Change.Action = "UPSERT"
        # CREATE: Creates a resource record set that has the specified values.
        # DELETE: Deletes an existing resource record set that has the specified values.
        # UPSERT: If a resource record set doesn't already exist, AWS creates it. If it does, Route 53 updates it with values in the request.
    $Change.ResourceRecordSet = New-Object Amazon.Route53.Model.ResourceRecordSet
    $Change.ResourceRecordSet.Name = "$Name.$Domain"
    $Change.ResourceRecordSet.Type = $Type
    $Change.ResourceRecordSet.TTL = $TTL
    #$Change.ResourceRecordSet.ResourceRecords.Add(@{Value=$Value})
    $Change.ResourceRecordSet.ResourceRecords.Add(@{Value=if ($Type -eq "TXT") {"""$Value"""} else {$Value}})

    # Get hosted zone
    $HostedZone = Get-R53HostedZones | Where-Object {$_.Name -eq $DomainDot}

    # Set final parameters and execute
    $Parameters = @{
        HostedZoneId = $HostedZone.Id
        ChangeBatch_Change = $Change # Object
        ChangeBatch_Comment = $Comment # "Edited A record"
    }
    Edit-R53ResourceRecordSet @Parameters
}
Function Start-CMInstance            {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        $Region,
        [Parameter(Mandatory=$true)]
        $InstanceID,
        $DomainName
    )
    $ErrorActionPreference = "Stop"
    $AllRegions       = (Get-AWSRegion).Region
    If ($AllRegions -notcontains $Reg) {
        Write-Error "$Region is not a valid AWS Region, Valid regions are $AllRegions"
    }
    $StartingInstance = Start-EC2Instance -InstanceId $InstanceID -Region $Region
    $PreviousState    = ($StartingInstance).PreviousState.Name.Value
    $CurrentState     = ($StartingInstance).CurrentState.Name.Value
    Write-Host "Instance $InstanceID $PreviousState --> $CurrentState"
    Start-Sleep 1
    If ($DomainName){
        Write-Verbose "Getting Public DNS name"
        $RunningInstance  = (Get-EC2Instance -InstanceId $InstanceID -Region $Region).RunningInstance
        $NameTag          = $RunningInstance.Tags | Where-Object {$_.Key -eq "Name"} | Select -ExpandProperty Value
        $PublicCName      = $RunningInstance.PublicDnsName
        Write-Output "Updating R53"
        Set-R53Record -Domain $DomainName -Type CNAME -Name $NameTag -Value $PublicCName -TTL 30 | Out-Null
    }
}
Function Stop-AllInstances           {
    $AllRegions = (Get-AWSRegion).Region
    foreach ($Region in $AllRegions) {
        $Instances = (Get-EC2Instance -Region $Region).RunningInstance
        foreach ($Instance in $Instances)
        {
            $Tags          = ($Instances.tags).Key
            $InstanceState = ($Instances.State).Name
            if ($Tags -notcontains "Persistent" -and $InstanceState -ne "stopped")
            {
                $InstanceID = ($Instances).InstanceId
                Write-Output "Stopping $InstanceID"
                Stop-EC2Instance -InstanceId $InstanceID -Region $Region | Out-Null
            }
        }
    }
}
Function Send-SSMPowerShellCommand   {
    [Cmdletbinding()]
    Param(
        [Parameter(Mandatory               =$true)]
        [string]   $Region,
        [Parameter(Mandatory               =$true,
            ValueFromPipeline              =$true,
            ValueFromPipelineByPropertyName=$true)]
        [string[]] $InstanceID,
        [Parameter(Mandatory               =$true)]
        [string]   $Command
    )
    BEGIN {}
    PROCESS {
        $CommandID            = (Send-SSMCommand -Region $Region -InstanceId $InstanceId -DocumentName "AWS-RunPowerShellScript" -Parameter @{commands="$Command"}).CommandId
        $SSMRunStatus         = $false
        While (!$SSMRunStatus) {
            Start-Sleep -Seconds 1
            $SSMCommandStatus = (Get-SSMCommand -InstanceId $InstanceId -CommandId $CommandID).Status.Value
            if ($SSMCommandStatus -eq "Success") {
                (Get-SSMCommandInvocationDetail -Region $Region -InstanceId $InstanceId -CommandId $CommandID).StandardOutputContent
                $SSMRunStatus = $true
            } elseif ($SSMCommandStatus -eq "Failed") {
                $SSMRunStatus = $true
                Write-Error "SSM Run Command to Failed"
            }
        }
    }
    END{}
}
Function Connect-RemoteDesktop       {
<#
.SYNOPSIS
Function to connect an RDP session without the password prompt
     
.DESCRIPTION
This function provides the functionality to start an RDP session without having to type in the password
     
.PARAMETER ComputerName
This can be a single computername or an array of computers to which RDP session will be opened
 
.PARAMETER User
The user name that will be used to authenticate
 
.PARAMETER Password
The password that will be used to authenticate
 
.PARAMETER Credential
The PowerShell credential object that will be used to authenticate against the remote system
 
.PARAMETER Admin
Sets the /admin switch on the mstsc command: Connects you to the session for administering a server
 
.PARAMETER MultiMon
Sets the /multimon switch on the mstsc command: Configures the Remote Desktop Services session monitor layout to be identical to the current client-side configuration
 
.PARAMETER FullScreen
Sets the /f switch on the mstsc command: Starts Remote Desktop in full-screen mode
 
.PARAMETER Public
Sets the /public switch on the mstsc command: Runs Remote Desktop in public mode
 
.PARAMETER Width
Sets the /w:<width> parameter on the mstsc command: Specifies the width of the Remote Desktop window
 
.PARAMETER Height
Sets the /h:<height> parameter on the mstsc command: Specifies the height of the Remote Desktop window
 
.NOTES
Name: Connect-RemoteDesktop
Author: Jaap Brasser
DateUpdated: 2016-10-28
Version: 1.2.5
Blog: http://www.jaapbrasser.com
 
.LINK
http://www.jaapbrasser.com
 
.EXAMPLE
. .\Connect-RemoteDesktop.ps1
     
Description
-----------
This command dot sources the script to ensure the Connect-Mstsc function is available in your current PowerShell session
 
.EXAMPLE
Connect-RemoteDesktop -ComputerName server01 -User contoso\jaapbrasser -Password (ConvertTo-SecureString 'supersecretpw' -AsPlainText -Force)
 
Description
-----------
A remote desktop session to server01 will be created using the credentials of contoso\jaapbrasser
 
.EXAMPLE
Connect-RemoteDesktop server01,server02 contoso\jaapbrasser (ConvertTo-SecureString 'supersecretpw' -AsPlainText -Force)
 
Description
-----------
Two RDP sessions to server01 and server02 will be created using the credentials of contoso\jaapbrasser
 
.EXAMPLE
server01,server02 | Connect-RemoteDesktop -User contoso\jaapbrasser -Password supersecretpw -Width 1280 -Height 720
 
Description
-----------
Two RDP sessions to server01 and server02 will be created using the credentials of contoso\jaapbrasser and both session will be at a resolution of 1280x720.
 
.EXAMPLE
server01,server02 | Connect-Mstsc -User contoso\jaapbrasser -Password (ConvertTo-SecureString 'supersecretpw' -AsPlainText -Force) -Wait
 
Description
-----------
RDP sessions to server01 will be created, once the mstsc process is closed the session next session is opened to server02. Using the credentials of contoso\jaapbrasser and both session will be at a resolution of 1280x720.
 
.EXAMPLE
Connect-Mstsc -ComputerName server01:3389 -User contoso\jaapbrasser -Password (ConvertTo-SecureString 'supersecretpw' -AsPlainText -Force) -Admin -MultiMon
 
Description
-----------
A RDP session to server01 at port 3389 will be created using the credentials of contoso\jaapbrasser and the /admin and /multimon switches will be set for mstsc
 
.EXAMPLE
Connect-Mstsc -ComputerName server01:3389 -User contoso\jaapbrasser -Password (ConvertTo-SecureString 'supersecretpw' -AsPlainText -Force) -Public
 
Description
-----------
A RDP session to server01 at port 3389 will be created using the credentials of contoso\jaapbrasser and the /public switches will be set for mstsc
 
.EXAMPLE
Connect-Mstsc -ComputerName 192.168.1.10 -Credential $Cred
 
Description
-----------
A RDP session to the system at 192.168.1.10 will be created using the credentials stored in the $cred variable.
 
.EXAMPLE
Get-AzureVM | Get-AzureEndPoint -Name 'Remote Desktop' | ForEach-Object { Connect-Mstsc -ComputerName ($_.Vip,$_.Port -join ':') -User contoso\jaapbrasser -Password (ConvertTo-SecureString 'supersecretpw' -AsPlainText -Force) }
 
Description
-----------
A RDP session is started for each Azure Virtual Machine with the user contoso\jaapbrasser and password supersecretpw
 
.EXAMPLE
PowerShell.exe -Command "& {. .\Connect-Mstsc.ps1; Connect-Mstsc server01 contoso\jaapbrasser (ConvertTo-SecureString 'supersecretpw' -AsPlainText -Force) -Admin}"
 
Description
-----------
An remote desktop session to server01 will be created using the credentials of contoso\jaapbrasser connecting to the administrative session, this example can be used when scheduling tasks or for batch files.
#>

    [cmdletbinding(SupportsShouldProcess,DefaultParametersetName='UserPassword')]
    param (
        [Parameter(Mandatory=$true,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true,
            Position=0)]
        [Alias('CN')]
            [string[]]     $ComputerName,
        [Parameter(ParameterSetName='UserPassword',Mandatory=$true,Position=1)]
        [Alias('U')] 
            [string]       $User,
        [Parameter(ParameterSetName='UserPassword',Mandatory=$true,Position=2)]
        [Alias('P')] 
            [string]       $Password,
        [Parameter(ParameterSetName='Credential',Mandatory=$true,Position=1)]
        [Alias('C')]
            [PSCredential] $Credential,
        [Alias('A')]
            [switch]       $Admin,
        [Alias('MM')]
            [switch]       $MultiMon,
        [Alias('F')]
            [switch]       $FullScreen,
        [Alias('Pu')]
            [switch]       $Public,
        [Alias('W')]
            [int]          $Width,
        [Alias('H')]
            [int]          $Height,
        [Alias('WT')]
            [switch]       $Wait
    )

    begin {
        [string]$MstscArguments = ''
        switch ($true) {
            {$Admin}      {$MstscArguments += '/admin '}
            {$MultiMon}   {$MstscArguments += '/multimon '}
            {$FullScreen} {$MstscArguments += '/f '}
            {$Public}     {$MstscArguments += '/public '}
            {$Width}      {$MstscArguments += "/w:$Width "}
            {$Height}     {$MstscArguments += "/h:$Height "}
        }

        if ($Credential) {
            $User     = $Credential.UserName
            $Password = $Credential.GetNetworkCredential().Password
        }
    }
    process {
        foreach ($Computer in $ComputerName) {
            $ProcessInfo = New-Object System.Diagnostics.ProcessStartInfo
            $Process = New-Object System.Diagnostics.Process
            
            # Remove the port number for CmdKey otherwise credentials are not entered correctly
            if ($Computer.Contains(':')) {
                $ComputerCmdkey = ($Computer -split ':')[0]
            } else {
                $ComputerCmdkey = $Computer
            }

            $ProcessInfo.FileName    = "$($env:SystemRoot)\system32\cmdkey.exe"
            $ProcessInfo.Arguments   = "/generic:TERMSRV/$ComputerCmdkey /user:$User /pass:$($Password)"
            $ProcessInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
            $Process.StartInfo       = $ProcessInfo
            if ($PSCmdlet.ShouldProcess($ComputerCmdkey,'Adding credentials to store')) {
                [void]$Process.Start()
            }

            $ProcessInfo.FileName    = "$($env:SystemRoot)\system32\mstsc.exe"
            $ProcessInfo.Arguments   = "$MstscArguments /v $Computer"
            $ProcessInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Normal
            $Process.StartInfo       = $ProcessInfo
            if ($PSCmdlet.ShouldProcess($Computer,'Connecting mstsc')) {
                [void]$Process.Start()
                if ($Wait) {
                    $null = $Process.WaitForExit()
                }       
            }
        }
    }
}

Set-Alias li   Get-CMEC2Instances 
Set-Alias iac  Invoke-AWSCommand
Set-Alias tts  Convert-TextToSpeech
Set-Alias npe  New-CMEasyPassword