Trouble setting the Visibility property on Cmdlets, etc. in a constrained endpt

Boe wrote a great series of articles on constrained endpoints as a guest on the Hey Scripting Guy blog which inspired me.

I decided to try the approach of using a startup script to secure commands in the constrained endpoint.

Unfortunately my customer is typing adverse and I had to resort to making my solution menu driven to minimize typing.

I am attempting to break their grip on the mouse by keeping all activity in the PowerShell host. i.e. no popup graphical elements, in the hope that some of braver souls may actually try typing a few cmdlets.

(Yes, a switch statement would be better for the “Are You sure” cases below, but this is a work in progress :slight_smile: )

Unfortunately, when my constrained endpoint’s start up script gets to the $_.Visibility = ‘Private’ sections, my screen bleeds over and over errors similar to:
The property ‘Visibility’ cannot be found on this object. Verify that the property exists and can be set.
At D:\scripts\Constrained_startup.ps1:264 char:9

  • $Command.Visibility = ‘Private’
  • CategoryInfo : InvalidOperation: (:slight_smile: , RuntimeException
  • FullyQualifiedErrorId : PropertyAssignmentException

I must be missing something.

Tips and feedback are more than welcome!

Marty Wiedmeyer

Here’s what the script looks like:

$PVER = "ScheduledTasksProfile 1.01 - 20141023 - 14:32 Central"

# *** Variables *** 
New-Variable -name temp -value $([io.path]::gettemppath())
Set-Variable -Name PSEmailServer -Value smtp-out.us.Nuex.com 
#Set-Variable -Name PSSessionConfigurationName -value WithProfile
New-Variable -Name VIMPATH -Value "D:\Nuex\apps\vim\vim74\vim.exe"
New-Variable -Name WD -Value "D:\Nuex\Work\"

# *** Update the PATH to include our scripts directory for the current user only, in their current sesson ***
$env:PATH += ";D:\Nuex\Scripts;D:\Nuex\Apps;D:\Nuex\Apps\vim\vim74"

# Add the custom formatting files to the session, placing them at the beginning of the path
Update-FormatData -PrependPath D:\Nuex\Modules\Nuex.format.ps1xml

# Add the shared Module pdir to the $PSModulePath
$env:PSModulePath = $env:PSModulePath + ";D:\Nuex\Modules"

# Change the Path permantly for the entire machine
#[Environment]::SetEnvironmentVariable("PATH", "D:\Nuex\Scripts;" + $env:Path, 'Machine')

# *** Alias ***

New-Alias -name lst -value List-ScheduledTask 
New-Alias -name m -value Show-NuexTaskCommands
New-Alias -name stast -value Start-ScheduledTask 
New-Alias -name stost -value Stop-ScheduledTask 
New-Alias -name cst -value Control-ScheduledTask 
New-Alias -name raie -value Restart-AIE
New-Alias -name rap -value Restart-ARPlugin
New-Alias -name rar -value Restart-ARrecond 

$NuexTaskCommands = @" 
=========================
Scheduled Task Management
=========================
Control-Scheduledtask [cst] -Control Nuexv3 Scheduled Tasks

==============================
Process and Service Management
==============================
Restart-AIE [raie] -Restart AIE
Restart-ARPlugin [rap] -Restart arplugin
Restart-ARrecond [rar] -Restart arrecond

"@

#Define Custom Proxy functions
Function Set-Nuexlog
  {
    $Script:GetTime = get-date -Format "HH:mm"
    $Script:Today = get-date -Format "yyyyMMdd"
    $Script:LogFile = "D:\Nuex\work\logs\$Today.txt"
  }  # End function Set-NuexLog

Function Write-NuexLog
  {

  Set-NuexLog
  $ServerName = $env:COMPUTERNAME
  $RemoteConnection=$PSSenderInfo|select -expandProperty ConnectionString
  $RemoteUser=$PSSenderInfo|select -expandProperty ConnectedUser
  
  Write-Output "$ServerName,$MenuItem,$env:UserName,$Script:GetTime,$Script:Today,$Script:CmdArgs,$RemoteUser,$RemoteConnection"|Out-File $Script:LogFile -append
  
  $Script:CmdArgs = $null
  } # End Function Write-NuexLog

function Prompt 
    {
    if ([System.IntPtr]::Size -eq 8) {$size = '64 bit'}
    else {$size = '32 bit'}  
    
    $CurrentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
    $SecPrin = New-Object Security.Principal.WindowsPrincipal $CurrentUser
    
    if
    ($SecPrin.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
    {$Admin = 'Administrator'}
    else {$Admin = 'non-Administrator'}
    
    if ($Host.Name -eq "ServerRemoteHost")
       {$Remote = "[REMOTE SESSION]"
        $MachineName = $null
       }
    else {$Remote = $null
          $MachineName = "$env:COMPUTERNAME"
         }

    $host.ui.RawUI.WindowTitle = "$Reminder   $Admin    $Size    $Remote"
    #"$MachineName :: $(get-location) 
    "$MachineName :: m for the menu
PS>"

     } # End function Prompt

Function Restart-AIE
    {
      $PlugAns = Read-Host "You are about to restart the AIE Service"
      stop-Service -DisplayName *AIE* -Confirm
      $MenuItem = "Restart-AIE"
      Write-NuexLog
    } # end function Restart-AIE


Function Restart-ARPlugin
    {
      $PlugAns = Read-Host "You are about to restart arplugin"
      stop-process -Name arplugin -Confirm
      $MenuItem = "Restart-ARPlugin"
      Write-NuexLog
    } # end function Restart-ARPlugin

Function Restart-ARrecond
    {
      $PlugAns = Read-Host "You are about to restart ARrecond"
      stop-process -Name ARrecond -Confirm
      $MenuItem = "Restart-ARrecond"
      Write-NuexLog
    } # end function Restart-ARrecond

Function Show-NuexTaskCommands
    {
      $NuexTaskCommands
      $MenuItem = "Show-NuexTaskCommands"
      Write-NuexLog
    } # end function Show-NuexTaskCommands

Function List-ScheduledTask
  {
   get-scheduledtask "*Remedyload*"
  }

Function Control-ScheduledTask
    {
    $Script:CmdArgs=$null
    $TodaysDate = Get-date -format yyyyMMdd-hhmm
    $ANS= $Null

        $Target = $Env:COMPUTERNAME

        Write-Output "Nuexv3 Tasks on $Target :"
        get-scheduledtask "*Remedyload*" | select TaskName, State|format-table -AutoSize


    $TargetTask = Read-Host "Enter (copy/paste or type) the name of the task to affect"
    
    $Action = Read-Host "St(A)rt, St(O)p, (E)nable or (D)isable $TargetTask"
      
      switch ($Action)
        {
          A
            {
             Start-Scheduledtask $TargetTask
            }
          e
            {
              Enable-Scheduledtask $TargetTask
            }
          d
            {
              Write-Output "You are about to Disable $TargetTask -Are you sure? Ctrl-C to exit"
              pause
              Disable-Scheduledtask $TargetTask
            }
          o
            {
              Write-Output "You are about to Stop $TargetTask  -Are you sure? Ctrl-C to exit"
              pause
              Stop-Scheduledtask $TargetTask
            }
        default
            {
              Write-Output "Please make a valid entry A, O, E or D. Exiting...`n" 
              break
            }
        }
            

        

    $Script:CmdArgs=$Target
    $MenuItem = "Update-ScheduledTask"
     Write-NuexLog

      } #End Function Control-ScheduledTask

Function Control-ScheduledTaskRemote
    {
    $Script:CmdArgs=$null
    $TodaysDate = Get-date -format yyyyMMdd-hhmm
    $ANS= $Null
    $ANS = Read-Host "Control a Scheduled Task in (D)ev or (S)IT"

    switch ($ANS)
      {
        D
          {
            $Target = "NUVDWITSM3AP21"
          }
        S
          {
            $Target = "NUVSWITSM3AP21"
          }
        }

        Write-Output "Nuexv3 Tasks on $Target :"
        invoke-command -comp $Target -scriptblock {get-scheduledtask "*Remedyload*"}


    $TargetTask = "Enter the name of the Task to affect"

    $Action = Read-Host "St(A)rt or St(O)p The Service"
      
      switch ($Action)
        {
          A
            {
             invoke-command -comp $Target -scriptblock {Start-Scheduledtask $TargetTask}
            }
          o
            {
              invoke-command -comp $Target -scriptblock {Stop-Scheduledtask $TargetTask}
            }
        }

        

    $Script:CmdArgs=$Target
    $MenuItem = "Update-ScheduledTaskRemote"
     Write-NuexLog

      } #End Function Control-ScheduledTaskRemote


#Proxy functions

[string[]]$proxyFunction = 'Update-ScheduledTask','Show-NuexTaskCommands','Get-Command','Restart-AIE','Restart-ARPlugin','Restart-ARrecond','List-ScheduledTask','Control-ScheduledTask','Prompt','Write-NuexLog','Set-NuexLog'

#Variables

Get-Variable | ForEach {   

    $_.Visibility = 'Private'

}

#Aliases

Get-Alias | ForEach {   

    $_.Visibility = 'Private'

}

#Cmdlets

ForEach ($Command in (Get-Command|Select-String "Measure-Object" -notmatch)) {

    If (($proxyFunction -notcontains $Command.Name)) {

        $Command.Visibility = 'Private'

    }

}

$ExecutionContext.SessionState.Applications.Clear()

$ExecutionContext.SessionState.Scripts.Clear()

$ExecutionContext.SessionState.LanguageMode = "NoLanguage"


$NuexTaskCommands

This line:

ForEach [$Command in [Get-Command|Select-String "Measure-Object" -notmatch]] {

Is returning back an object of Microsoft.PowerShell.Commands.MatchInfo which is not exactly the System.Management.Automation.CmdletInfo object that you actually need in order to set the visibility to private.
I’m not sure what is happening here with the measure-object and select-string stuff, but if you remove all of that and just leave the Get-Command call, it should work properly.

ForEach [$Command in [Get-Command] {
    If [[$proxyFunction -notcontains $Command.Name]] {
        $Command.Visibility = 'Private'
    }
}

It sounds like you are still in the dev phase of this startup script, but just wanted to make sure that you know to filter out your aliases and variables that should not be hidden (basically anything that you have created in the script) so the rest of the endpoint works properly.

What Boe said.

Marty - are you trying to allow interactive access to that endpoint? That’s the only thing that stands out about measure-object to me, although you would need proxies for the following functions as well:

$iss = [Management.Automation.Runspaces.InitialSessionState]::CreateRestricted("RemoteServer")
$iss.Commands | where { $_.Visibility -eq "Public" } | Select Name

Hi CM and Boe,

Yes, I want the endpoint to be used interactively.

I’ll pull the measure-object and select-string stuff out at your suggestion.

It makes sense that the object type returned would be different.

Also, I’ll try adding the $iss lines as well, CM.

Thank you both for the replies!

I’ll let you know how it goes.

Marty

Hi Guys,

Your suggestions really helped out.

My constrained endpoint is working like a champ!

Thanks for your help.

Marty