Function not working when called from within switch

This topic contains 1 reply, has 2 voices, and was last updated by Profile photo of Curtis Smith Curtis Smith 2 weeks, 2 days ago.

  • Author
    Posts
  • #74730
    Profile photo of Jared
    Jared
    Participant

    Long story short; our users each get an "S:" drive which acts as their home network share. My problem, these folders are never cleaned up after the employee leaves. I need a way to automate the migration of these dead directories to an archive location where they will be held until deletion.

    I've wrote a script (I'm a newb) that should help with this. It has it's own little menu which I really like because it would be nice for the person who will be running this. The first part of this script gets all dir names that match my regex. It then checks to see if the dir names appear in AD. Each dir name is then placed in a System.Object based on the result of the AD lookup. I have 3 of these:
    $LinkGoodCollection = Employee acct still exists in AD
    $LinkOKCollection = Employee acct is disabled
    $LinkBadCollection = No acct found (the directory name could not be matched to any AD samaccountname)

    So If I run this and choose option 2 or 3 it will migrate everything just fine. The problem is that after migrating the data my System.Object variables need to be updated. Because if someone chooses option 1 (the report option) it will still show the contents of the $LinkGoodCollection, $LinkOKCollection & $LinkBadCollection PRIOR to preforming the migrate. So it reports old data. I tried to create a function "Refresh-Data" that should go out and re-run the dir sorting process in the the Good, OK & Bad variables. But it doesn't work. I get no errors or anything to say that the "Refresh-Data" function failed.

    Thanks in advance for any help!

    #BEGIN ENV VARIABLES------>
                    $OU = 'TBD'
                    $regex = '^[a-zA-Z]{7}$'
                    $path = 'C:\Users\TEST\Desktop\Test\'
                    $ArchiveLocation = '\\archive\archive\S-Drive Archive'
    
                    $LinkGoodCollection = New-Object System.Collections.Generic.List[System.Object]
                    $LinkOKCollection = New-Object System.Collections.Generic.List[System.Object]
                    $LinkBadCollection = New-Object System.Collections.Generic.List[System.Object]
                         
                    
    
                    $SdriveFolders = Get-ChildItem $path `
                    -Directory | 
                    where name -match $regex | 
                    select -ExpandProperty name
    #
    function Show-Menu
    {
         param (
               [string]$Title = 'S DRIVE CONSISTENCY CHECK'
         )
         cls
         Write-Host "================ $Title ================"
         
         Write-Host "1: SHOW S: DRIVE USER REPORT"
         Write-Host "2: MIGRATE DISABLED USERS S: DRIVE"
         Write-Host "3: MIGRATE DELETED USERS S: DRIVE"
         Write-Host "4: SETTINGS"
         Write-Host "Q: Press 'Q' to quit…"
    }
    
    Function Show-Settings
    {
        param (
               [string]$SettingsTitle = 'SETTINGS'
         )
         cls
         Write-Host "================ $SettingsTitle ================"
         
         Write-Host "1: CHANGE OU SCOPE: " -NoNewline 
         Write-Host "($OU)"
         Write-Host "2: CHANGE REGEX MATCHING PATTERN: " -NoNewline 
         Write-Host "($REGEX)"
         Write-Host "3: CHANGE ARCHIVE LOCATION: " -NoNewline 
         Write-Host "($ArchiveLocation)"
         Write-Host "Q: Press 'Q' to return to main menu…"
    }
    
    Function Refresh-Data
    {
        Clear-Variable LinkGoodCollection -Force
        Clear-Variable LinkOKCollection -Force
        Clear-Variable LinkBadCollection -Force
        Clear-Variable DirName -Force
        Clear-Variable SdriveFolders -Force
    
        $LinkGoodCollection = New-Object System.Collections.Generic.List[System.Object]
        $LinkOKCollection = New-Object System.Collections.Generic.List[System.Object]
        $LinkBadCollection = New-Object System.Collections.Generic.List[System.Object]
    
        $SdriveFolders = Get-ChildItem $path `
                    -Directory | 
                    where name -match $regex | 
                    select -ExpandProperty name
    
        $i = foreach ($DirName in $SdriveFolders){
                        $AcctFound = Get-ADUser -LDAPFilter "(samaccountname=$DirName)" -ErrorAction SilentlyContinue
        
                            if ($AcctFound.Enabled -eq $true){$LinkGoodCollection.Add($DirName)}
                            
                            if ($AcctFound.Enabled -eq $false){$LinkOKCollection.Add($DirName)}
    
                            if ($AcctFound -eq $null){$LinkBadCollection.Add($DirName)}
                        }    
    }
    
    function Pause-Script
    {
       Read-Host 'Press enter to continue…' | Out-Null
    }
    #
    
                $i = foreach ($DirName in $SdriveFolders){
                        $AcctFound = Get-ADUser -LDAPFilter "(samaccountname=$DirName)" -ErrorAction SilentlyContinue
        
                            if ($AcctFound.Enabled -eq $true){$LinkGoodCollection.Add($DirName)}
                            
                            if ($AcctFound.Enabled -eq $false){$LinkOKCollection.Add($DirName)}
    
                            if ($AcctFound -eq $null){$LinkBadCollection.Add($DirName)}
                        }
    
    # 
    do
    {
         Show-Menu
         $input = Read-Host "Please make a selection"
         switch ($input)
         {
               '1' {
                     cls
                        $i = foreach ($DirName in $SdriveFolders){
                        $AcctFound = Get-ADUser -LDAPFilter "(samaccountname=$DirName)" -ErrorAction SilentlyContinue
        
                            if ($AcctFound.Enabled -eq $true){
                                $FullName = Get-ADUser -Identity $DirName | select -ExpandProperty name -ErrorAction SilentlyContinue
                                Write-Host "$path$DirName : IS LINKED TO AN AD USER ACCT (COMPANY\$FullName)" -ForegroundColor Green
                            }
    
                            if ($AcctFound.Enabled -eq $false){
                                $FullName = Get-ADUser -Identity $DirName | select -ExpandProperty name -ErrorAction SilentlyContinue
                                write-host "$path$DirName : USER IS DISABLED BUT STILL HAS AN S DRIVE (COMPANY\$FullName)" -ForegroundColor Yellow
                            }
    
                            if ($AcctFound -eq $null){
                                write-host "$path$DirName : NO USER ACCOUNT FOUND FOR THIS DIRECTORY" -ForegroundColor Red
                            }
                        }
                    Write-host "Number of S:\ drives without an owner: " ($LinkBadCollection.Count)
                    Write-host "Number of S:\ drives owned by disabled AD users: " ($LinkOKCollection.Count)
                    Write-host "Number of S:\ drives owned by active AD users: " ($LinkGoodCollection.Count)
        
                    Pause-Script
                }
    
                '2' {
                      cls
    
                        Write-Host "Checking archive status '$ArchiveLocation'"
    
                        if((test-path $ArchiveLocation) -eq $true){Write-Host "Path is good…" -ForegroundColor Green}
                        
                        if((test-path $ArchiveLocation) -eq $false){Write-Host "Default path ($ArchiveLocation) is not accessible." -ForegroundColor Red
                            Do {$ArchiveLocation = Read-Host -Prompt "Please supply a new archive location…"}
                                Until ((Test-Path $ArchiveLocation) -eq $true)}
    
                            foreach ($DirName in $LinkOKCollection){
                                write-host "Migrating disabled user ($DirName's) S: drive to the archive location…" -ForegroundColor Green
                                New-Item -Path $ArchiveLocation -name $DirName -ItemType "directory"
                                robocopy.exe "$Path$DirName" "$ArchiveLocation\$DirName" /log+:"$ArchiveLocation\Robocopy Logs\$DirName.txt" --% /e /copy:datou /move /r:1 /w:2 /np | Out-Null
                            }
                            Write-Host "Migration completed successfully" -ForegroundColor Green
                            Refresh-Data
                            Pause-Script
                }
    
                '3'{
                     cls
    
                        Write-Host "Checking archive status '$ArchiveLocation'"
    
                        if((test-path $ArchiveLocation) -eq $true){Write-Host "Path is good…" -ForegroundColor Green}
                        
                        if((test-path $ArchiveLocation) -eq $false){Write-Host "Default path ($ArchiveLocation) is not accessible." -ForegroundColor Red
                            Do {$ArchiveLocation = Read-Host -Prompt "Please supply a new archive location…"} 
                                Until ((Test-Path $ArchiveLocation) -eq $true)}
    
                            foreach ($DirName in $LinkBadCollection){
                                write-host "Migrating disabled user ($DirName's) S: drive to the archive location…" -ForegroundColor Green
                                New-Item -Path $ArchiveLocation -name $DirName -ItemType "directory"
                                robocopy.exe "$Path$DirName" "$ArchiveLocation\$DirName" /log+:"$ArchiveLocation\Robocopy Logs\$DirName.txt" --% /e /copy:datou /move /r:1 /w:2 /np | Out-Null
                            }
                            Write-Host "Migration completed successfully" -ForegroundColor Green
                            Refresh-Data
                            Pause-Script
                
                }
    
                '4' {
                      cls
    
                      do{
                        Show-Settings
                        $SettingInput = Read-Host "Please make a selection"
                        switch ($SettingInput)
    
                        {
                        '1'
                            {$OU = Read-Host "Please enter a new OU: "
                            Show-Settings
                            }
    
                        '2' {$regex = Read-Host "Please enter a new REGEX: "
                            Show-Settings
                            }
    
                        '3' {$ArchiveLocation = Read-Host "Please enter a new archive location: "
                            Show-Settings
                            }
                        }
                        }until ($SettingInput -eq 'q' )
                }
        }
    }until ($input -eq 'q')
    #< ------END SWITCHING
    
  • #74733
    Profile photo of Curtis Smith
    Curtis Smith
    Participant

    Hey Jared,
    Your issue is likely because of your scope. When you set your variables inside of the function that are only relevant to the function, not to the rest of the script. For more information about scopes see about_scopes (https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_scopes)

    For Example

    Remove-Variable test -ErrorAction SilentlyContinue
    $test = 123
    
    Function testfun {
        Write-Host "Inside Before: $test"
        $test = 234
        Write-Host "Inside After: $test"
    }
    Write-Host "Before: $test"
    Testfun
    Write-Host "After: $test"

    Results:

    Before: 123
    Inside Before: 123
    Inside After: 234
    After: 123

    As you can see, when I set the value of $test inside of the function, it maintained the value set inside of the function; however, after the function ended, the $test variable was still it's original value.

    To facilitate what you are trying to do, you can change the scope of your variable when you set it, so that it works with the variable outside of it's normal scope.

    For Example:

    Remove-Variable test -ErrorAction SilentlyContinue
    $test = 123
    
    Function testfun {
        Write-Host "Inside Before: $test"
        $script:test = 234
        Write-Host "Inside After: $test"
    }
    Write-Host "Before: $test"
    Testfun
    Write-Host "After: $test"

    Results:

    Before: 123
    Inside Before: 123
    Inside After: 234
    After: 234

    Since I instructed the script to use the variable in the scope of the script, the variable maintained the new value even after the function ended.

    This should help you accomplish what you are trying to do; however, I personally do not believe using variable scopes like this to be in good practice as it makes your functions much harder to reuse in other scripts. You should be able to write your functions where you provide information to and get results from the function. The function should not write values to variables outside of its own scope.

    For Example: I'd be more inclined to do something like this

    Remove-Variable test -ErrorAction SilentlyContinue
    $variables = @{
        test1 = 123;
        test2 = 123;
        test3 = 123
    }
    
    Function updateVars {
        @{
            test1 = 234;
            test2 = 345;
            test3 = 456
        }
    }
    Write-Host "Before: $($variables['test1']), $($variables['test2']), $($variables['test3'])"
    $variables = updateVars -value1 $test1 -value2 ([ref]$test2) -value3 ([ref]$test3)
    Write-Host "After: $($variables['test1']), $($variables['test2']), $($variables['test3'])"

    Results:

    Before: 123, 123, 123
    After: 234, 345, 456

    Or maybe use a pscustomobject rather than a hash table

    Hope that helps

You must be logged in to reply to this topic.