PowerCLI

 View Only
Expand all | Collapse all

Trouble passing a function to be executed with Invoke-VMScript

LucD

LucDMar 24, 2014 06:49 AM

markvm2

markvm2Mar 24, 2014 07:00 AM

  • 1.  Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 24, 2014 02:54 AM

    I have the below function that I need to run on the remote VM through Invoke-VMScript with "remove-profile -days 0 - -computername localhost -profiles $profilename" this script removes a local profile, you can find out more about it here http://gallery.technet.microsoft.com/scriptcenter/Remove-Profile-787d9188

    Now I am having extreme difficultly figuring out how to drop the function with Invoke-VMScript so I can call the function. I usually do "Invoke-VMScript -VM $vmname2 -ScriptText "powershell.exe commands" and it works great, but I can't figure out how to drop a function to that powershell process to call the function. Any ideas? I do not want to do this with a ps1 file or remote share, I want this function to be available to the powershell process i start so I can call it. I have tried a bunch of different things like script blocks and invoke-command but they always break my entire script.

    To make it more clear - When i start a powershell process through Invoke-VMScript, I need this function to be there so I can call it. So once the powershell.exe process is running i can just have it execute "remove-profile -days 0 - -computername localhost -profiles $profilename"

    function remove-profile { [cmdletbinding(

            SupportsShouldProcess = $true,

            ConfirmImpact="High"

        )]

        param(

            [string[]]$computername="localhost",

            [validaterange(0,9999)][int]$days = 1,

            [string[]]$exceptions = $null,

            [string[]]$profiles = $null

        )

        #function to get profiles

        function Get-Profile {

            [cmdletbinding()]

            param(

                [string]$computername,

                [string[]]$exceptions = $null,

                [string[]]$profiles = $null

            )

            #function to convert array of strings into regex for paths to avoid

            function build-Regex {

                param([string[]]$items)

                $( foreach($item in $items){ "^$item$" } ) -join "|"

            }

            $date = get-date

            #test connection

            if(Test-Connection -ComputerName $computername -BufferSize 16 -count 2 -Quiet){

        

                #Check OS

                Try{

                    $opSys = Get-WmiObject Win32_OperatingSystem -computername $computername | select -ExpandProperty version

                }

                Catch{

                    Throw "Error: Could not obtain OS version WMI information from $computername"

                    Return

                }

                #Find XP profiles

                if ($opSys –like “5*”) {

                

                    #define property that we will use to test exceptions / profile regexs against

                    $property = "fullname"

            

                    #get all profiles on computer using file system

                    $allProfiles = Get-Childitem "\\$computername\c$\documents and settings\" -force | ?{$_.PSIsContainer}

                }

                #Find Vista + profiles

                if ($opSys –like “6*”) {

                

                    #define property that we will use to test exceptions / profile regexs against

                    $property = "localpath"

                

                    Try{

                        #get all profiles on computer using WMI

                        $allProfiles = get-wmiobject -computername $computername -class win32_userprofile | ?{ $_.localpath -like "C:\users\*" }

                    }

                    Catch{

                        Throw "Error gathering profile WMI information from $computername.  Be sure that WMI is functioning on this system and that it is running Windows Vista or Server 2008 or later"

                        Return

                    }

                }

                #if specified, filter for profiles

                if($profiles){

                    #build regex using provided profiles

                    $profileRegex = build-Regex $profiles

                    #test profiles against profiles regex

                    $allProfiles = $allProfiles | ?{ $(split-path $_.$property -leaf) -match $profileRegex }

                }

                #if specified, filter exceptions

                if($exceptions){

                    #build regex using provided exceptions

                    $exceptionsRegex = build-Regex $exceptions

                    #test profiles against exceptions regex

                    $allProfiles = $allProfiles | ?{ $(split-path $_.$property -leaf) -notmatch $exceptionsRegex }

                }

                #Return results

                $allProfiles

            }

            else{

                Throw "Could not connect to $computername"

                Return

            }

        }

        #Get date for profile last access comparison

        $date = get-date

        #Add standard accounts to exclude unless explicitly instructed not to

        if( -not $DontExcludeStandardAccounts ){

            $exceptions += "Administrator", "LocalService", "NetworkService", "All Users", "Default User"

        }

        #loop through provided computers

        foreach($computer in $computername){

        

            #get all the profiles for this computer

            $profilesToRemove = Get-Profile -computername $computer -exceptions $exceptions -profiles $profiles -ErrorAction stop

        

            #if none returned, throw an error and move on to the next computer

            if(-not $profilesToRemove){

                Write-Error "Error: No profiles returned on $computer"

                Continue

            }

            #Get-Profiles returns a directoryinfo object for XP, use this to determine OS.

            if($profilesToRemove[0] -isnot [System.IO.DirectoryInfo]){ $opsys = 6 }

            else{ $opsys = 5 }

            #loop through profiles

            foreach($profile in $profilesToRemove){

        

                #Define path and last access time for profile

                if($opsys -eq 6){

                    #Windows 7: convert localpath to remote path remote path.  Currently only handling profiles on C drive

                    $path = $profile.localpath.replace("C:","\\$computer\C$")

                    $lastAccess = ([WMI]'').ConvertToDateTime($profile.LastUseTime)

                }

                else{

                    #Windows XP: define path

                    $path = $profile.fullname

                    $lastAccess = $profile.lastWriteTime

                }

                #Confirm we can reach $path

                Try {

                    get-item $path -force -ErrorAction stop | out-null

                }

                Catch{

                    #if we couldnt get the item, display an error and move on to the next profile

                    Write-Error "Error: Could not get-item for $path"

                    Continue

                }

                #If the profile is older than the days specified, remove it

                if($lastAccess -lt $date.AddDays(-$days)){

            

                    #-confirm and -whatif support

                    if($pscmdlet.shouldprocess("$path last accessed $lastAccess")){

                    

                        #build results object

                        $tempResult = "" | Select ComputerName, Path, lastAccess, Status

                   

                        Try{

                            if($opsys -eq 6){

                                #Windows Vista+: remove the profile using WMI

                                $profile.delete()

                            }

                            else{

                                #Windows XP: remove the profile using file system

                                Remove-Item $path -force -confirm:$false -recurse

                            }

                            #Add properties to results object

                            $tempResult.ComputerName = $computer

                            $tempResult.Path = $path

                            $tempResult.LastAccess = $lastAccess

                            $tempResult.Status = "No error"

                        }

                        Catch{

                        

                            #add properties to results object

                            $tempResult.ComputerName = $computer

                            $tempResult.Path = $path

                            $tempResult.LastAccess = $lastAccess

                            $tempResult.Status = "Error removing profile"

                        

                            #WMI delete method or file removal failed.  Write an error, move on to the next profile

                            Write-Error "Error: Could not delete $path last accessed $lastAccess"

                            Continue

                        }

                    

                        #display result

                        $tempResult

                    }

                }

            }

        }

    }



  • 2.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 24, 2014 05:50 AM

    One way of doing is, is to place the function and the call to the function in a here-string, and then pass that string on the ScriptText parameter.

    $myFunction = @"

    function remove-profile { [cmdletbinding(

    # The rest of the function

    }

    # Call the function

    remove-profile

    "@

    Invoke-VMscript -VM $vm -ScriptText $myFunction -ScriptType PowerShell



  • 3.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 24, 2014 06:15 AM

    That does not seem to work. The function does not get called, it just seems to spit the text of the string out? in ISE all of the text is brown so I am not sure if that is ok?



  • 4.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 24, 2014 06:27 AM

    The text is marked as brown in the ISE because the editor sees the complete text as a string (which it is, it is a here-string).

    Try this simplified example

    $myFunction = @"
    function Test-MyFunction {
      "Inside my function"
    }
    Test-MyFunction
    "@


    $vm = Get-VM -Name MyVM
    Invoke-VMScript -VM $vm -ScriptText $myFunction -ScriptType PowerShell

    The output should be something like this



  • 5.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 24, 2014 06:35 AM

    LucD,

    I am doing just as you described and even tried the example you just gave me. However, it does not execute the function. The script just spits out the text in $myFuction.




  • 6.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 24, 2014 06:37 AM

    Perhaps you can attach a screenshot ?



  • 7.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 24, 2014 06:46 AM

    I put that insto a ps1 file and run it. I hope this is not some silly thing and I'm an idiot. I am still new to PS.

    $myFunction = @"

    function Test-MyFunction {

      "Inside my function"

    }

    Test-MyFunction

    "@

    $myFunction

    Read-Host "ok"



  • 8.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 24, 2014 06:49 AM

    But where is the Invoke-VMScript ?



  • 9.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 24, 2014 06:51 AM

    I was just testing it out before doing the Invoke-VMScript. It should work the same way right?



  • 10.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 24, 2014 06:53 AM

    Yes, it should display the text that is inside the function



  • 11.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 24, 2014 06:56 AM

    Any ideas why it won't? PowerShell version problem? Why would it not work on my system..



  • 12.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 24, 2014 06:59 AM

    But where do you actually run the code ?

    I don't see the PowerShell prompt, nor do I see you calling the .ps1 file.



  • 13.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 24, 2014 07:00 AM

    I run the ps1 file with PowerShell.



  • 14.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 24, 2014 08:35 AM

    But from where and how do you run the .ps1 file ?

    From the PowerCLI prompt, from the PowerShell prompt, from the PowerShell ISE ?

    I would like to see how you run the script and the output.



  • 15.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 24, 2014 12:52 PM

    This is interesting to me too, i'll follow you guys.

    I think markvm2 is right-clicking the ps1 file and "Run with Powershell" LucD =)



  • 16.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 25, 2014 12:26 AM

    LucD,

    I have been right clicking the ps1 file and running with PowerShell.

    I am going to try with Invoke-VMScript tonight.



  • 17.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 25, 2014 12:55 AM

    Ok for some reason the example script you gave me seems to work ok through Invoke-VMScript. I don't know why?

    Anyways, the script I am trying to send is not working with some odd errors -

    Here is the Script -

    Add-PSSnapin VMware.VimAutomation.Core


    Connect-VIServer -Server 192.168.1.206


    $myFunction = @"
    function Remove-Profile {
    [cmdletbinding(
            SupportsShouldProcess = $true,
            ConfirmImpact="High"
        )]
        param(
            [string[]]$computername="localhost",
            [validaterange(0,9999)][int]$days = 1,
            [string[]]$exceptions = $null,
            [string[]]$profiles = $null
        )

        #function to get profiles
        function Get-Profile {
            [cmdletbinding()]
            param(
                [string]$computername,
                [string[]]$exceptions = $null,
                [string[]]$profiles = $null
            )

            #function to convert array of strings into regex for paths to avoid
            function build-Regex {
                param([string[]]$items)
                $( foreach($item in $items){ "^$item$" } ) -join "|"
            }

            $date = get-date

            #test connection
            if(Test-Connection -ComputerName $computername -BufferSize 16 -count 2 -Quiet){

                #Check OS
                Try{
                    $opSys = Get-WmiObject Win32_OperatingSystem -computername $computername | select -ExpandProperty version
                }
                Catch{
                    Throw "Error: Could not obtain OS version WMI information from $computername"
                    Return
                }

                #Find XP profiles
                if ($opSys –like “5*”) {

                    #define property that we will use to test exceptions / profile regexs against
                    $property = "fullname"

                    #get all profiles on computer using file system
                    $allProfiles = Get-Childitem "\\$computername\c$\documents and settings\" -force | ?{$_.PSIsContainer}
                }

                #Find Vista + profiles
                if ($opSys –like “6*”) {

                    #define property that we will use to test exceptions / profile regexs against
                    $property = "localpath"

                    Try{
                        #get all profiles on computer using WMI
                        $allProfiles = get-wmiobject -computername $computername -class win32_userprofile | ?{ $_.localpath -like "C:\users\*" }
                    }
                    Catch{
                        Throw "Error gathering profile WMI information from $computername.  Be sure that WMI is functioning on this system and that it is running Windows Vista or Server 2008 or later"
                        Return
                    }

                }

                #if specified, filter for profiles
                if($profiles){
                    #build regex using provided profiles
                    $profileRegex = build-Regex $profiles

                    #test profiles against profiles regex
                    $allProfiles = $allProfiles | ?{ $(split-path $_.$property -leaf) -match $profileRegex }
                }

                #if specified, filter exceptions
                if($exceptions){
                    #build regex using provided exceptions
                    $exceptionsRegex = build-Regex $exceptions

                    #test profiles against exceptions regex
                    $allProfiles = $allProfiles | ?{ $(split-path $_.$property -leaf) -notmatch $exceptionsRegex }
                }

                #Return results
                $allProfiles

            }
            else{
                Throw "Could not connect to $computername"
                Return
            }
        }

        #Get date for profile last access comparison
        $date = get-date

        #Add standard accounts to exclude unless explicitly instructed not to
        if( -not $DontExcludeStandardAccounts ){
            $exceptions += "Administrator", "LocalService", "NetworkService", "All Users", "Default User"
        }

        #loop through provided computers
        foreach($computer in $computername){

            #get all the profiles for this computer
            $profilesToRemove = Get-Profile -computername $computer -exceptions $exceptions -profiles $profiles -ErrorAction stop

            #if none returned, throw an error and move on to the next computer
            if(-not $profilesToRemove){
                Write-Error "Error: No profiles returned on $computer"
                Continue
            }

            #Get-Profiles returns a directoryinfo object for XP, use this to determine OS.
            if($profilesToRemove[0] -isnot [System.IO.DirectoryInfo]){ $opsys = 6 }
            else{ $opsys = 5 }

            #loop through profiles
            foreach($profile in $profilesToRemove){

                #Define path and last access time for profile
                if($opsys -eq 6){

                    #Windows 7: convert localpath to remote path remote path.  Currently only handling profiles on C drive
                    $path = $profile.localpath.replace("C:","\\$computer\C$")
                    $lastAccess = ([WMI]'').ConvertToDateTime($profile.LastUseTime)

                }
                else{

                    #Windows XP: define path
                    $path = $profile.fullname
                    $lastAccess = $profile.lastWriteTime
                }

                #Confirm we can reach $path
                Try {
                    get-item $path -force -ErrorAction stop | out-null
                }
                Catch{
                    #if we couldn't get the item, display an error and move on to the next profile
                    Write-Error "Error: Could not get-item for $path"
                    Continue
                }

                #If the profile is older than the days specified, remove it
                if($lastAccess -lt $date.AddDays(-$days)){

                    #-confirm and -whatif support
                    if($pscmdlet.shouldprocess("$path last accessed $lastAccess")){

                        #build results object
                        $tempResult = "" | Select ComputerName, Path, lastAccess, Status

                        Try{

                            if($opsys -eq 6){
                                #Windows Vista+: remove the profile using WMI
                                $profile.delete()
                            }
                            else{
                                #Windows XP: remove the profile using file system
                                Remove-Item $path -force -confirm:$false -recurse
                            }

                            #Add properties to results object
                            $tempResult.ComputerName = $computer
                            $tempResult.Path = $path
                            $tempResult.LastAccess = $lastAccess
                            $tempResult.Status = "No error"
                        }
                        Catch{

                            #add properties to results object
                            $tempResult.ComputerName = $computer
                            $tempResult.Path = $path
                            $tempResult.LastAccess = $lastAccess
                            $tempResult.Status = "Error removing profile"

                            #WMI delete method or file removal failed.  Write an error, move on to the next profile
                            Write-Error "Error: Could not delete $path last accessed $lastAccess"
                            Continue
                        }

                        #display result
                        $tempResult
                    }
                }
            }
        }
       }
    Remove-Profile -computername localhost -profiles NAGKFWMZWKUBRT
    "@
    $myFunction

    Invoke-VMScript -VM Server2K81 -ScriptText $myFunction

    Read-Host "ok"

    PS C:\Users\mark\Desktop> C:\Users\mark\Desktop\heret2.ps1
    Add-PSSnapin : Cannot add Windows PowerShell snap-in VMware.VimAutomation.Core because it is already added. Verify the name of the snap-in and try again.
    At C:\Users\mark\Desktop\heret2.ps1:1 char:13
    + Add-PSSnapin <<<<  VMware.VimAutomation.Core
        + CategoryInfo          : InvalidArgument: (VMware.VimAutomation.Core:String) [Add-PSSnapin], PSArgumentException
        + FullyQualifiedErrorId : AddPSSnapInRead,Microsoft.PowerShell.Commands.AddPSSnapinCommand


    Name                           Port  User                         
    ----                           ----  ----                         
    192.168.1.206                  443   homelab\mark                 
    Split-Path : Cannot bind argument to parameter 'Path' because it is null.
    At C:\Users\mark\Desktop\heret2.ps1:81 char:11
    + split-path <<<<  $_.$property -leaf
        + CategoryInfo          : InvalidData: (:) [Split-Path], ParameterBindingValidationException
        + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.SplitPathCommand

    Split-Path : Cannot bind argument to parameter 'Path' because it is null.
    At C:\Users\mark\Desktop\heret2.ps1:90 char:11
    + split-path <<<<  $_.$property -leaf
        + CategoryInfo          : InvalidData: (:) [Split-Path], ParameterBindingValidationException
        + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.SplitPathCommand

    function Remove-Profile {
    [cmdletbinding(
            SupportsShouldProcess = True,
            ConfirmImpact="High"
        )]
        param(
            [string[]]="localhost",
            [validaterange(0,9999)][int] = 1,
            [string[]] = ,
            [string[]] =
        )

        #function to get profiles
        function Get-Profile {
            [cmdletbinding()]
            param(
                [string],
                [string[]] = ,
                [string[]] =
            )

            #function to convert array of strings into regex for paths to avoid
            function build-Regex {
                param([string[]])
                ^$ -join "|"
            }

             = get-date

            #test connection
            if(Test-Connection -ComputerName  -BufferSize 16 -count 2 -Quiet){

                #Check OS
                Try{
                     = Get-WmiObject Win32_OperatingSystem -computername  | select -ExpandProperty version
                }
                Catch{
                    Throw "Error: Could not obtain OS version WMI information from "
                    Return
                }

                #Find XP profiles
                if ( –like “5*”) {

                    #define property that we will use to test exceptions / profile regexs against
                     = "fullname"

                    #get all profiles on computer using file system
                     = Get-Childitem "\\\c$\documents and settings\" -force | ?{.PSIsContainer}
                }

                #Find Vista + profiles
                if ( –like “6*”) {

                    #define property that we will use to test exceptions / profile regexs against
                     = "localpath"

                    Try{
                        #get all profiles on computer using WMI
                         = get-wmiobject -computername  -class win32_userprofile | ?{ .localpath -like "C:\users\*" }
                    }
                    Catch{
                        Throw "Error gathering profile WMI information from .  Be sure that WMI is functioning on this system and that it is running Windows Vista
    or Server 2008 or later"
                        Return
                    }

                }

                #if specified, filter for profiles
                if(){
                    #build regex using provided profiles
                     = build-Regex

                    #test profiles against profiles regex
                     =  | ?{  -match  }
                }

                #if specified, filter exceptions
                if(){
                    #build regex using provided exceptions
                     = build-Regex

                    #test profiles against exceptions regex
                     =  | ?{  -notmatch  }
                }

                #Return results


            }
            else{
                Throw "Could not connect to "
                Return
            }
        }

        #Get date for profile last access comparison
         = get-date

        #Add standard accounts to exclude unless explicitly instructed not to
        if( -not  ){
             += "Administrator", "LocalService", "NetworkService", "All Users", "Default User"
        }

        #loop through provided computers
        foreach( in ){

            #get all the profiles for this computer
             = Get-Profile -computername  -exceptions  -profiles  -ErrorAction stop

            #if none returned, throw an error and move on to the next computer
            if(-not ){
                Write-Error "Error: No profiles returned on "
                Continue
            }

            #Get-Profiles returns a directoryinfo object for XP, use this to determine OS.
            if([0] -isnot [System.IO.DirectoryInfo]){  = 6 }
            else{  = 5 }

            #loop through profiles
            foreach(C:\Users\mark\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1 in ){

                #Define path and last access time for profile
                if( -eq 6){

                    #Windows 7: convert localpath to remote path remote path.  Currently only handling profiles on C drive
                     = C:\Users\mark\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1.localpath.replace("C:","\\\C$")
                     = ([WMI]'').ConvertToDateTime(C:\Users\mark\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1.LastUseTime)

                }
                else{

                    #Windows XP: define path
                     = C:\Users\mark\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1.fullname
                     = C:\Users\mark\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1.lastWriteTime
                }

                #Confirm we can reach
                Try {
                    get-item  -force -ErrorAction stop | out-null
                }
                Catch{
                    #if we couldn't get the item, display an error and move on to the next profile
                    Write-Error "Error: Could not get-item for "
                    Continue
                }

                #If the profile is older than the days specified, remove it
                if( -lt .AddDays(-)){

                    #-confirm and -whatif support
                    if(.shouldprocess(" last accessed ")){

                        #build results object
                         = "" | Select ComputerName, Path, lastAccess, Status

                        Try{

                            if( -eq 6){
                                #Windows Vista+: remove the profile using WMI
                                C:\Users\mark\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1.delete()
                            }
                            else{
                                #Windows XP: remove the profile using file system
                                Remove-Item  -force -confirm:False -recurse
                            }

                            #Add properties to results object
                            .ComputerName =
                            .Path =
                            .LastAccess =
                            .Status = "No error"
                        }
                        Catch{

                            #add properties to results object
                            .ComputerName =
                            .Path =
                            .LastAccess =
                            .Status = "Error removing profile"

                            #WMI delete method or file removal failed.  Write an error, move on to the next profile
                            Write-Error "Error: Could not delete  last accessed "
                            Continue
                        }

                        #display result

                    }
                }
            }
        }
       }
    Remove-Profile -computername localhost -profiles NAGKFWMZWKUBRT

    VM           : Server2K81
    ExitCode     : 1
    ScriptOutput :
    Uid          : /VIServer=homelab\mark@192.168.1.206:443/VirtualMachine=VirtualMachine-vm-34/VMScriptResult=371857150_1/
    Length       : 0

    The Output -



  • 18.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 25, 2014 02:36 AM

    If i put the function in singles quotes with @' '@ the errors go away but the script seems to go back to just spitting out the text, even with Invoke-VMScript.

    Strangely, if i run it in ISE, I can then call the function outside of the here string and it works.

    btw for anyone that wants to try this, the proper syntax for the function is "Remove-Profile -days 0  -computername localhost -profiles profilename confirm:$false"



  • 19.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 25, 2014 06:07 AM

    In the last lines of your script, you have

    $myFunction

    Invoke-VMScript -VM Server2K81 -ScriptText $myFunction

    The first line explains why the script spits out the complete function.

    Leave out that line.

    I'm curious to see what Invoke-VMScript produces as output.

    You should at least get back some lines, even when there is no output produced.

    As you should be able to see with my test function



  • 20.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 25, 2014 11:10 AM

    I don't get anything back.

    This is what it looks like -

    VM       : Server2K81
    ExitCode : 1

    ScriptOutput :

    Uid      : /VIServer=homelab\mark@192.168.1.206:443/VirtualMachine=VirtualMachine-vm-34/VMScriptResult=371857150_1/
    Length   : 0


  • 21.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 25, 2014 12:56 PM

    The ExitCode = 1 seems to indicate something goes wrong with the function on the target machine.

    Does the function execute on Server2K81 directly ?



  • 22.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 25, 2014 11:20 PM

    It does not execute locally. I am reading that single quotes won't even work. I need to use double quotes but I get those split path errors?



  • 23.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 26, 2014 01:32 AM

    The Invoke-Expression method I have works locally, but not through Invoke-VMScript.

    Below i tried to use the encodedcommand method and I am getting the error where it says it can't find the PowerShell interpreter.

    Add-PSSnapin VMware.VimAutomation.Core

    Connect-VIServer -Server 192.168.1.206

    $command2 = {

    $sc1 = @'

    function remove-profile {

    [cmdletbinding(

            SupportsShouldProcess = $true,

            ConfirmImpact="High"

        )]

        param(

            [string[]]$computername="localhost",

            [validaterange(0,9999)][int]$days = 1,

            [string[]]$exceptions = $null,

            [string[]]$profiles = $null

        )  

        #function to get profiles

        function Get-Profile {

            [cmdletbinding()]

            param(

                [string]$computername,

                [string[]]$exceptions = $null,

                [string[]]$profiles = $null

            )   

            #function to convert array of strings into regex for paths to avoid

            function build-Regex {

                param([string[]]$items)

                $( foreach($item in $items){ "^$item$" } ) -join "|"

            }

            $date = get-date

            #test connection

            if(Test-Connection -ComputerName $computername -BufferSize 16 -count 2 -Quiet){     

                #Check OS

                Try{

                    $opSys = Get-WmiObject Win32_OperatingSystem -computername $computername | select -ExpandProperty version

                }

                Catch{

                    Throw "Error: Could not obtain OS version WMI information from $computername"

                    Return

                }

                #Find XP profiles

                if ($opSys –like “5*”) {

                   

                    #define property that we will use to test exceptions / profile regexs against

                    $property = "fullname"

               

                    #get all profiles on computer using file system

                    $allProfiles = Get-Childitem "\\$computername\c$\documents and settings\" -force | ?{$_.PSIsContainer}

                }

                #Find Vista + profiles

                if ($opSys –like “6*”) {      

                    #define property that we will use to test exceptions / profile regexs against

                    $property = "localpath"               

                    Try{

                        #get all profiles on computer using WMI

                        $allProfiles = get-wmiobject -computername $computername -class win32_userprofile | ?{ $_.localpath -like "C:\users\*" }

                    }

                    Catch{

                        Throw "Error gathering profile WMI information from $computername.  Be sure that WMI is functioning on this system and that it is running Windows Vista or Server 2008 or later"

                        Return

                                        }

                }

                #if specified, filter for profiles

                if($profiles){

                    #build regex using provided profiles

                    $profileRegex = build-Regex $profiles

                    #test profiles against profiles regex

                    $allProfiles = $allProfiles | ?{ $(split-path $_.$property -leaf) -match $profileRegex }

                }

                #if specified, filter exceptions

                if($exceptions){

                    #build regex using provided exceptions

                    $exceptionsRegex = build-Regex $exceptions

                    #test profiles against exceptions regex

                    $allProfiles = $allProfiles | ?{ $(split-path $_.$property -leaf) -notmatch $exceptionsRegex }

                }

                #Return results

                $allProfiles

            }

            else{

                Throw "Could not connect to $computername"

                Return

            }

        }

        #Get date for profile last access comparison

        $date = get-date 

        #Add standard accounts to exclude unless explicitly instructed not to

        if( -not $DontExcludeStandardAccounts ){

            $exceptions += "Administrator", "LocalService", "NetworkService", "All Users", "Default User"

        }

        #loop through provided computers

        foreach($computer in $computername){       

           #get all the profiles for this computer

            $profilesToRemove = Get-Profile -computername $computer -exceptions $exceptions -profiles $profiles -ErrorAction stop       

            #if none returned, throw an error and move on to the next computer

            if(-not $profilesToRemove){

                Write-Error "Error: No profiles returned on $computer"

                Continue

            }

            #Get-Profiles returns a directoryinfo object for XP, use this to determine OS.

            if($profilesToRemove[0] -isnot [System.IO.DirectoryInfo]){ $opsys = 6 }

            else{ $opsys = 5 }

            #loop through profiles

            foreach($profile in $profilesToRemove){     

                #Define path and last access time for profile

                if($opsys -eq 6){

                    #Windows 7: convert localpath to remote path remote path.  Currently only handling profiles on C drive

                    $path = $profile.localpath.replace("C:","\\$computer\C$")

                    $lastAccess = ([WMI]'').ConvertToDateTime($profile.LastUseTime)

                }

                else{

                    #Windows XP: define path

                    $path = $profile.fullname

                    $lastAccess = $profile.lastWriteTime

                }

                #Confirm we can reach $path

                Try {

                    get-item $path -force -ErrorAction stop | out-null

                }

                Catch{

                    #if we couldnt get the item, display an error and move on to the next profile

                    Write-Error "Error: Could not get-item for $path"

                    Continue

                }

                #If the profile is older than the days specified, remove it

                if($lastAccess -lt $date.AddDays(-$days)){

               

                    #-confirm and -whatif support

                    if($pscmdlet.shouldprocess("$path last accessed $lastAccess")){

                       

                        #build results object

                        $tempResult = "" | Select ComputerName, Path, lastAccess, Status

                      

                        Try{

                            if($opsys -eq 6){

                                #Windows Vista+: remove the profile using WMI

                                $profile.delete()

                            }

                            else{

                                #Windows XP: remove the profile using file system

                                Remove-Item $path -force -confirm:$false -recurse

                            }

                            #Add properties to results object

                            $tempResult.ComputerName = $computer

                            $tempResult.Path = $path

                            $tempResult.LastAccess = $lastAccess

                            $tempResult.Status = "No error"

                        }

                        Catch{

                           

                            #add properties to results object

                            $tempResult.ComputerName = $computer

                            $tempResult.Path = $path

                            $tempResult.LastAccess = $lastAccess

                            $tempResult.Status = "Error removing profile"

                           

                            #WMI delete method or file removal failed.  Write an error, move on to the next profile

                            Write-Error "Error: Could not delete $path last accessed $lastAccess"

                            Continue

                        }

                       

                        #display result

                        $tempResult

                    }

                }

            }

        }

    }

    remove-profile -days 0 -computername localhost -profiles AAFNCRRHOBXXJI -confirm:$false

    '@

    Invoke-Expression $sc1

    }

    $command3 = "ipconfig"

    $bytes2 = [System.Text.Encoding]::Unicode.GetBytes($command2)

    $encodedcommand = [Convert]::Tobase64String($bytes2)

    Invoke-VMScript -VM Server2K81 -ScriptText "powershell.exe -EncodedCommand $encodedcommand"

    Read-Host "ok"



  • 24.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 26, 2014 01:43 AM

    So this works locally with the Invoke-Expressions method..Now I just need to figure out how to drop it to the VM with Invoke-VMScript?

    $sc1 = @'

    function remove-profile {

    [cmdletbinding(

            SupportsShouldProcess = $true,

            ConfirmImpact="High"

        )]

        param(

            [string[]]$computername="localhost",

            [validaterange(0,9999)][int]$days = 1,

            [string[]]$exceptions = $null,

            [string[]]$profiles = $null

        )  

        #function to get profiles

        function Get-Profile {

            [cmdletbinding()]

            param(

                [string]$computername,

                [string[]]$exceptions = $null,

                [string[]]$profiles = $null

            )   

            #function to convert array of strings into regex for paths to avoid

            function build-Regex {

                param([string[]]$items)

                $( foreach($item in $items){ "^$item$" } ) -join "|"

            }

            $date = get-date

            #test connection

            if(Test-Connection -ComputerName $computername -BufferSize 16 -count 2 -Quiet){     

                #Check OS

                Try{

                    $opSys = Get-WmiObject Win32_OperatingSystem -computername $computername | select -ExpandProperty version

                }

                Catch{

                    Throw "Error: Could not obtain OS version WMI information from $computername"

                    Return

                }

                #Find XP profiles

                if ($opSys –like “5*”) {

                   

                    #define property that we will use to test exceptions / profile regexs against

                    $property = "fullname"

               

                    #get all profiles on computer using file system

                    $allProfiles = Get-Childitem "\\$computername\c$\documents and settings\" -force | ?{$_.PSIsContainer}

                }

                #Find Vista + profiles

                if ($opSys –like “6*”) {      

                    #define property that we will use to test exceptions / profile regexs against

                    $property = "localpath"               

                    Try{

                        #get all profiles on computer using WMI

                        $allProfiles = get-wmiobject -computername $computername -class win32_userprofile | ?{ $_.localpath -like "C:\users\*" }

                    }

                    Catch{

                        Throw "Error gathering profile WMI information from $computername.  Be sure that WMI is functioning on this system and that it is running Windows Vista or Server 2008 or later"

                        Return

                                        }

                }

                #if specified, filter for profiles

                if($profiles){

                    #build regex using provided profiles

                    $profileRegex = build-Regex $profiles

                    #test profiles against profiles regex

                    $allProfiles = $allProfiles | ?{ $(split-path $_.$property -leaf) -match $profileRegex }

                }

                #if specified, filter exceptions

                if($exceptions){

                    #build regex using provided exceptions

                    $exceptionsRegex = build-Regex $exceptions

                    #test profiles against exceptions regex

                    $allProfiles = $allProfiles | ?{ $(split-path $_.$property -leaf) -notmatch $exceptionsRegex }

                }

                #Return results

                $allProfiles

            }

            else{

                Throw "Could not connect to $computername"

                Return

            }

        }

        #Get date for profile last access comparison

        $date = get-date 

        #Add standard accounts to exclude unless explicitly instructed not to

        if( -not $DontExcludeStandardAccounts ){

            $exceptions += "Administrator", "LocalService", "NetworkService", "All Users", "Default User"

        }

        #loop through provided computers

        foreach($computer in $computername){       

           #get all the profiles for this computer

            $profilesToRemove = Get-Profile -computername $computer -exceptions $exceptions -profiles $profiles -ErrorAction stop       

            #if none returned, throw an error and move on to the next computer

            if(-not $profilesToRemove){

                Write-Error "Error: No profiles returned on $computer"

                Continue

            }

            #Get-Profiles returns a directoryinfo object for XP, use this to determine OS.

            if($profilesToRemove[0] -isnot [System.IO.DirectoryInfo]){ $opsys = 6 }

            else{ $opsys = 5 }

            #loop through profiles

            foreach($profile in $profilesToRemove){     

                #Define path and last access time for profile

                if($opsys -eq 6){

                    #Windows 7: convert localpath to remote path remote path.  Currently only handling profiles on C drive

                    $path = $profile.localpath.replace("C:","\\$computer\C$")

                    $lastAccess = ([WMI]'').ConvertToDateTime($profile.LastUseTime)

                }

                else{

                    #Windows XP: define path

                    $path = $profile.fullname

                    $lastAccess = $profile.lastWriteTime

                }

                #Confirm we can reach $path

                Try {

                    get-item $path -force -ErrorAction stop | out-null

                }

                Catch{

                    #if we couldnt get the item, display an error and move on to the next profile

                    Write-Error "Error: Could not get-item for $path"

                    Continue

                }

                #If the profile is older than the days specified, remove it

                if($lastAccess -lt $date.AddDays(-$days)){

               

                    #-confirm and -whatif support

                    if($pscmdlet.shouldprocess("$path last accessed $lastAccess")){

                       

                        #build results object

                        $tempResult = "" | Select ComputerName, Path, lastAccess, Status

                      

                        Try{

                            if($opsys -eq 6){

                                #Windows Vista+: remove the profile using WMI

                                $profile.delete()

                            }

                            else{

                                #Windows XP: remove the profile using file system

                                Remove-Item $path -force -confirm:$false -recurse

                            }

                            #Add properties to results object

                            $tempResult.ComputerName = $computer

                            $tempResult.Path = $path

                            $tempResult.LastAccess = $lastAccess

                            $tempResult.Status = "No error"

                        }

                        Catch{

                           

                            #add properties to results object

                            $tempResult.ComputerName = $computer

                            $tempResult.Path = $path

                            $tempResult.LastAccess = $lastAccess

                            $tempResult.Status = "Error removing profile"

                           

                            #WMI delete method or file removal failed.  Write an error, move on to the next profile

                            Write-Error "Error: Could not delete $path last accessed $lastAccess"

                            Continue

                        }

                       

                        #display result

                        $tempResult

                    }

                }

            }

        }

    }

    remove-profile -days 0 -computername localhost -profiles AAFNCRRHOBXXJI -confirm:$false

    '@

    Invoke-Expression $sc1



  • 25.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 10, 2017 01:14 AM

    Hi All,

    I know this is an old thread - but I too was having this exact same problem - and have solved it (Huzzah!)

    There appears to be an undocumented Character limit for the -scripttext field - it's somewhere around the 2,000 - 3,000 character mark (I didn't have time to endlessly test it to find it) - so passing a simple function (with only a few lines of code) works without issue, passing a complex function fail.

    2 Things to add here:

    1: It would be really nice and save a LOT of headaches if the scripttext limit was documented somewhere

    2: The invoke-vmscript function should parse the -scripttext field and if it exceeds the limit, fail with an error message that is helpful "the scripttext field exceeds the character limit of XXXX - you may wish to streamline your script or transfer the content using the copy-vmguestfile function first"

    (or something)

    Thanks.



  • 26.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 10, 2017 08:04 AM

    Afaik, it's not related to the Invoke-VMScript cmdlet, but to the underlying OS and ScriptType you select.

    • Bat - the maximum DOS command line is 2047 (pre-Windows XP)
    • Bat - 8191 (Windows XP and later)
    • PowerShell - 8191
    • Bash - Depends on the distro and version. Nowadays it seems to be around 2097152 (check with 'getconf ARG_MAX')


  • 27.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 27, 2014 04:16 AM

    Well, I give up! I decided to use Copy-VMGuestFile instead and it is working just fine. I wanted this to be self contained in my script without the needs for any extra files or settings. So what I will do is have my script create the PS1 file, copy it to the VM with Copy-VMGuestFile, rune the script, delete it in the VM and then delete it on the host running the script.



  • 28.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 27, 2014 08:01 AM

    Isn't the error message stating where the problem is ?

    It seems to say that the account you use (GuestCredential) doesn't have the required permissions to run a script.

    Or perhaps the PowerShell engine is not configured to run scripts for that account.

    If you logon with that account on the target VM, can you start the PowerShell prompt and execute the script ?



  • 29.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 28, 2014 02:27 AM

    LucD,

    The account can run smaller commands through Invoke-VMScript, that is not the problem. btw, that error only happens when I send the command over encoded

    $bytes2 = [System.Text.Encoding]::Unicode.GetBytes($command2)

    $encodedcommand = [Convert]::Tobase64String($bytes2)

    Invoke-VMScript -VM Server2K81 -ScriptText "powershell.exe -EncodedCommand $encodedcommand"



  • 30.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 28, 2014 05:59 AM

    Try like this, that seems to work for me.

    $command2 = "Get-Process"
    $vm = Get-VM MyVM

    $bytes2 = [System.Text.Encoding]::Unicode.GetBytes($command2)
    $encodedcommand = [Convert]::Tobase64String($bytes2)
    Invoke-VMScript -VM $vm -ScriptText "powershell.exe -EncodedCommand $encodedcommand" -ScriptType bat


  • 31.  RE: Trouble passing a function to be executed with Invoke-VMScript

    Posted Mar 27, 2014 12:09 PM

    Agree Luc,

    I think mark have to change the approach to get things work. Maybe testing with simpler scripts and making a step by step with the desired script... That requires a lot of patience for sure!