Return live data from Invoke-Command? (While it's executing)

I have a remote server that doesnt have the modules needed for me to run the cmdlets I want directly on it (get-aduser) but I need to get some information from it before I can run the cmdlet I need on my local session (domain name, client id). I prefer to get this information directly from the server because it allows me to plug this information directly into the get-aduser cmdlet without having to do a foreach command to query across all domains (very time consuming when ran against thousands of servers)

Example:

 Invoke-Command -ComputerName $Server -ScriptBlock { 
        $CID = (Hostname).Split('-')[1]
        $ServerDomain = (Get-WmiObject Win32_ComputerSystem).Domain

Passed to:

$DisabledClientStaff = Get-Aduser -Server $ServerDomain -Filter "Enabled -eq '$False'" | 
Where DistinguishedName -like $CID | Select -ExpandProperty SamAccountName

I dont think there is a way for me to retrieve information from an invoke-command before the write-host output and I dont have any other way of filtering for the specific group of disabled users I need besides with the OU aka Client ID. Does this sound possible the way I am trying to do it? The only other ways I can currently think of are to 1) Run a 2nd invoke command of some sort (not ideal) or 2) Do a foreach and check all domains for a matching user. I’d also have to find a new way of obtaining the client id for the specific server the invoke command is currently being ran against which I dont know how thatd work either. Any feedback or tips would be appreciated.

Distinguished Name Ex:

DistinguishedName : CN=First Last,OU=858619760,OU=Clients,DC=XXX,DC=com

 

Thanks!

 

 

Does this line of code really work?

$CID = (Hostname).Split(‘-’)[1]
I wouldn’t expect it to. :wink:
Instead of explicilty remote to another computer and determining its domain you could utilize the implicit remoting abilites of the WMI/CIM cmdlets.
Get-CimInstance Win32_ComputerSystem -ComputerName $Server | Select-Object -ExpandProperty Domain

This way you wouldn’t need to explicitly remote to the remote computer at all. :wink:

BTW: You should not be using Get-WmiObject anymore. Use Get-CimInstance instead.

[quote quote=211707][/quote]

Yea server names are formatted like xxx-CID-01.domain.com so splitting “-” and selecting [1] gets CID every time.

Running get-cim or get-wmi outside of the Scriptblock would technically work but Id also be searching thousands of servers multiple times for individual pieces of information which is far from ideal due to timeliness thats why I thought itd be easier to grab it directly from the server that the scriptblock is being invoked against. What I’d like to accomplish is pulling the CID and Domain from the server being invoked against and sending it outside of the script block to use with get-aduser to find disabled users with that matching client id # “(Hostname).Split(‘-’)[1]”. Id then like to remotely call that back into the invoke command with USING so I can check disabled users vs C:\users to remove those directories. Sounds pretty wild and probably isnt possible but the way get-aduser lets you use the filter (cant filter with just the OU) and server parameters (one server at a time) means Id need to search 30 sub domains and tens of thousands accounts to find a match for every server the script is ran against (thousands). Makes a lot more sense at that level to tell it the specific domain and use where to filter with the -like operator for the matching OU.

OK. But what is “Hostname” in your code? Hostname without a dollar charachter is not a variable. Is it just a typo here in your example or do you manually change it in your productive code?

OK. So the snippet you showed above is just a small part of the original scriptblock, right? Anyway if the information you query from the remote computers all could received by CIM you could use cim sessions to wrap multiple Get-CimInstance commands and that should be faster than Invoke-Command.

Am I wrong or wouldn’t you get the hostname from the cmdlet “Get-CimInstance Win32_ComputerSystem” as well? :wink:

You know you don’t need the active directory module to query the AD, don’t you? :wink:

I have the impression you should make a step back start with explaining the task you want to accomplish and not the attempted solution you’re already working on. You might went a harder way than needed.

  1. Hostname isnt the variable… $CID Is :slight_smile: Im just storing the striped down server name in the variable $CID (e.g $CID = (Hostname).Split(‘-’)[1]).

  2. Yes its the part that I am having trouble with everything else is already working fine. I will look more into CIM sessions but I also need to do more then just retrieve the information I need to use it actively to target our internal staff user profiles and disabled user profiles for the OU so either way it seems like Id have to connect the servers multiple times (1 to get information, 2 to act on it) which is what I was hoping to avoid.

  3. I had no idea there was another way to query ad for user samaccounts other then with get-aduser. Id love to know another method if you have one to share. Ive pretty much explained what Im trying to accomplish above but let me share some extra details.

Steps to accomplish:

  1. Locate Internal Staff samaccount names - (Done in local session)
  2. Collect Server FQDNs - (Done in local session)
  3. Remotely connect to FQDNs to check disk space (Invoke-command)
  4. While invoked to each server find matching disabled users OU/FQDN (This is where I’m stuck)
  5. If free space -le 10 remove matching directories (Internal Staff and Disabled Users)

Extra Details: I am basically plucking the samaccountnames from AD and appending them to C:\Users and if disk space for the C:\ is -le to 10% it removes the matching directories. This has been tested and is working great with our internal staff (because I can get the info from my local session and pass with USING to invoke) but my thought was to also try and find disabled client users with matching OUs so I can check for those directories and remove them too. The problem though is when I invoke commands against my $servers variable I have no idea how I can effectively target each members disabled users specifically. That’s why I thought it made sense to just collect it from the server directly being invoked against at the time of checking disk space.

Duplicate

Duplicate

I think in this case the best solution ive came up with so far may just to be to collect all 15000+ inactive user accounts over 90 days old and just filter through the variable using the result of $CID. Seems to be the only real way of targeting disabled users for the specific server disk space is currently being checked on.

@Olaf - Updated a bit what do you think?

   $Domains = GetADTrustDomains -AllDatacenters
   $Servers = foreach ($D in $Domains) {
        # Collect all Servers               
        Get-ADComputer -Server $D -Filter * | Select-Object -ExpandProperty DNSHostName
    }

    $DisabledClientUsers = foreach ($D in $Domains) {
        Search-ADAccount –UsersOnly –AccountInActive –TimeSpan 90:00:00:00 -Server $D | Where Enabled -eq $False | Select SamAccountName,DistinguishedName }

    $InternalStaffProfiles = (Get-ADUser -Filter "SamAccountName -like '*.*'").SamAccountName
     Foreach ($I in $InternalStaff) { Join-Path -Path "C:\Users" -ChildPath "\$I" }

    $Results =

    Invoke-Command -ComputerName $Servers -ScriptBlock {
   
        $CID = (Hostname).Split('-')[1] 
        $DisabledClientUserProfiles = $USING:DisableClientUsers | Where DistinguishedName -like *$CID* | Select -ExpandProperty SamAccountName
        Foreach ($D in $DisabledClientProfiles) { Join-Path -Path "C:\Users" -ChildPath "\$D" }     
        $SystemDrive = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID = 'C:'"  
        $PercentageFree = [Math]::Round(($SystemDrive.FreeSpace / $SystemDrive.Size * 100),0) 
              
        # If C:\ Usage < 10% free remove Internal Staff / Disable Client Staff user profiles

        If ($PercentageFree -le 10) {
            Remove-Item '$USING:InternalStaffProfiles','$DisabledClientUserProfiles' }

        # If C:\ Usage > 10% free ignore

        Else { Out-Null 
    }

This is pretty much final my working code but Id love some feedback on it still if anyone has anything to share about formatting or layout or anything else that I am doing wrong or weird. I ended up moving what I could into functions and some stuff had to stay in the local session because the remote servers cant get the information needed

  1. ) Functions (not all are used due to the fact that remote servers cant run certain commands)
  2. ) Mostly final code
Function GetInternalStaffProfilePaths {
    Param(
        $Server = 'apcutil',
        $Filter = "SamAccountName -like '*.*'")
$InternalStaff = (Get-ADUser -Server $Server -Filter $Filter).SamAccountName
Foreach ($I in $InternalStaff) {
    Join-Path -Path "C:\Users" -ChildPath "\$I" 
}

}
Function GetDisabledClientUsers {
Param(
$TimeSpan = ‘90:00:00:00’)

foreach ($D in $Domains) {
    Search-ADAccount –UsersOnly –AccountInActive –TimeSpan $TimeSpan -Server $D | Where-Object Enabled -eq $False 
}

}
Function FormatProfilesToRemove {

$CID = (Hostname).Split('-')[1] 
$DisabledClientUsers = Get-DisabledClientUsers | Where-Object DistinguishedName -like *$CID* | Select-Object -ExpandProperty SamAccountName
Foreach ($D in $DisabledClientUsers) {
    Join-Path -Path "C:\Users" -ChildPath "\$D" 
}     

}
Function GetFreeCSpacePercentage {
Param(
[string]$ComputerName = ‘localhost’,
$Filter = “DeviceID = ‘C:’”)

$SystemDrive = Get-CimInstance Win32_LogicalDisk -ComputerName $Computername -Filter $Filter  
[Math]::Round(($SystemDrive.FreeSpace / $SystemDrive.Size * 100), 0) 

}

CmdletBinding()]
Param (
    [Parameter(Mandatory=$True)]
    [String]$ComputerName)

Try {

    $ErrorActionPreference = 'SilentlyContinue'

    Import-module R:\support\Libraries\PowerShell-Functions\Active-Directory.psm1
    Import-module R:\support\Libraries\PowerShell-Functions\Modules.psm1 -Force
    Import-module C:\Users\Dominguez.V\Desktop\Functions\VictorPS.psm1

    # Get all Domains
    $Domains = GetADTrustDomains -AllDatacenters

    # Find ALL Disabled Client Users inactive > 90 days
    $DisabledClientUsers = foreach ($D in $Domains) {
        Search-ADAccount –UsersOnly –AccountInActive –TimeSpan 90:00:00:00 -Server $D | Where-Object Enabled -eq $False | Select-Object SamAccountName, DistinguishedName 
    }   
    #Format Internal Staff User profile paths
    $InternalStaffUsers = (Get-ADUser -Filter "SamAccountName -like '*.*'").SamAccountName
    $InternalStaffUserProfiles = Foreach ($I in $InternalStaffUsers) { Join-Path -Path "C:\Users" -ChildPath "\$I" }

    $Results =

    Invoke-Command -ComputerName $ComputerName -ScriptBlock {
   
        $CID = (Hostname).Split('-')[1] 
        $DisabledClientUsers = $USING:DisabledClientUsers | Where-Object DistinguishedName -like *$CID* | Select-Object -ExpandProperty SamAccountName
        $DisabledClientUserProfiles = Foreach ($D in $DisabledClientUsers) { Join-Path -Path "C:\Users" -ChildPath "\$D" }

        # If C:\ Usage < 10% free remove Internal Staff profiles and Disabled client user profiles > 90 days inactive

        If (${Function:GetFreeCSpacePercentage} -le 10) {
            Remove-Item $USING:InternalStaffUserProfiles,$DisabledClientUserProfiles -Recurse -Force -Verbose 4>&1
        }

        # If C:\ Usage > 10% free ignore

        Else {
            Out-Null 
        } 
    }
    $Results
}                                                                                                                                                                                                                                                         
Catch { 
    Write-Warning "An error occurred:"
    Write-Host $error[0] -ForegroundColor Red -BackgroundColor Black
} 

Read-Host "Press ENTER to exit"