Problems with Variable (data not persistent)

This topic contains 7 replies, has 2 voices, and was last updated by  Stuart 2 months ago.

  • Author
    Posts
  • #96768

    Stuart
    Participant

    Hi All,

    I have a system object array defined as follows:

    $obj=new-object System.Object

    I am adding members of type NoteProperty into that and then appending it into a hash table called $Reportx.

    Working fine, however I also need to call a function called Check-Delegates. This also adds stuff into the $obj and attempts to append / save it into the same $Reportx hash table ($Reportx+=$obj)

    Debugging reveals the following:

    1) The $Reportx hash table is appended with content populated from $obj as seen in section #*** Building Report ***
    2) This is committed to CSV later at the end of the loop and everyone's happy.

    The problem is, when I call the Function Check-Delegates which also attempts a 'similar' sequence to step 1 above, it keeps being overwritten with the previous ForLoop variable data. I was expecting it to be appended and not overwritten.

    So the net result is, the CSV file contains content from the section #*** Building Report *** and NO CONTENT generated from the Function Check-Delegates.

    It's certain a school boy error, just I cant see it.

    Any Help appreciated as always

    Cheers

    Stuart

    $ScriptInfo = @"
    ================================================================================
    Export-MailboxMigrationDetails.ps1 | v1.0.0
    by Stuart
    ================================================================================
    SAMPLE SCRIPT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
    "@
    
    #==========================Creating variables and paths==========================#
    $PrevErrorActionPreference = $ErrorActionPreference
    $ErrorActionPreference = 'silentlycontinue'
    $path=Test-Path c:\O365Reports
    If ($path -like "False") {md c:\O365Reports} else {}
    $second=(get-date).Second
    $minute=(get-date).Minute
    $hour=(get-date).Hour
    $day=(get-date).Day
    $month=(get-date).Month
    $year=(get-date).Year
    $time="$hour.$minute.$second"
    $ReportPreText="AllMailboxSummary"
    $report2="$day.$Month.$year"
    $report="$ReportPreText-$report2-$time"
    $Reportx=@()
    $log="c:\O365Reports\log.txt"
    
    $IncludeMailboxAccess = $true
    $IncludeSendAs = $true
    $IncludeSendOnBehalf = $true
    $IncludeFolderDelegates = $true
    $IncludeCommonFoldersOnly = $true
    $DelegatesToSkip = "NT AUTHORITY\SELF","DOMAIN\BESADMIN","DOMAIN\Administrators"
    $ExpandSecurityGroups = $false
    $ExpandDistributionGroups = $false
    $IncludeEntireForest = $true
    $hash = @{} #This hash table is used by the Check-Delegates function
    
    
    
    #==========================Functions==========================#
    
    Function Connect-OnPremiseExchange{ 
    
        #Connects to Exchange On Premise, this will revive any existing sessions
        #Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File "C:\O365Reports\EXpassword.txt"
        $ComputerName = 'SH-VM-EXCH-01' 
        $password=get-content "C:\O365Reports\EXpassword.txt" | ConvertTo-SecureString
        $userid='stuart'
        $UserCredential=New-Object System.Management.Automation.PSCredential $userid,$password
    
        $Sessions = @( Get-PSSession | Where-Object {
        ( $_.computername -EQ $ComputerName ) -AND ( $_.State -EQ 'Opened' ) } )
    
        If ( $Sessions )
        {
        If ( $Sessions.Count -GT 1 )
        {
            $Session   = $Sessions | Select-Object -First 1
            $LeftOvers = $Sessions | Where-Object { -Not ( $_.Id -EQ $Session.Id ) }
            $LeftOvers | Remove-PSSession -ErrorAction SilentlyContinue | Out-Null
        }
    
        Else { $Session = $Sessions }
        }
        Else
        {
        $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$ComputerName/PowerShell/ -Authentication Kerberos -Credential $UserCredential # Create new session.
        }
    
        Import-PSSession $Session[0] -AllowClobber # Use this.
     } # Close Function
    
    Function Write-Log ($LogString) {
        $LogStatus = $LogString.Split(":")[0]
        If ($LogStatus -eq "SUCCESS") {
            Write-Host $LogString -ForegroundColor Green
            $LogString | Out-File $Log -Append  }
        If ($LogStatus -eq "INFO") {
            Write-Host "$LogString" -ForegroundColor Cyan
            $LogString | Out-File $Log -Append }
        If ($LogStatus -eq "ALERT") {
            Write-Host $LogString -ForegroundColor Yellow
            $LogString | Out-File $Log -Append }
        If ($LogStatus -eq "ERROR") {
            Write-Host $LogString -BackgroundColor Red
            $LogString | Out-File $Log -Append
            "`n" | Out-File $ErrorLog -Append
            $LogString | Out-File $Log -Append }
        If ($LogStatus -eq "AUDIT") {
            Write-Host $LogString -ForegroundColor DarkGray
            $LogString | Out-File $Log -Append  }
        If ($LogStatus -eq "") {
            Write-Host ""
            Write-Output "`n" | Out-File $RunLog -Append }
    }
    
    Function Check-Delegates ([string]$DelegateID) {
        $CheckDelegate = Get-Recipient $DelegateID -ErrorAction SilentlyContinue
    
        If ($CheckDelegate -eq $null) {
            $CheckDelegate = Get-Group $DelegateID -ErrorAction SilentlyContinue }
    
        If ($CheckDelegate -ne $null) {
            If (($CheckDelegate.RecipientType -like "Mail*" -and $ExpandDistributionGroups -eq $false) -or $CheckDelegate.RecipientType -like "*Mailbox") {
                $DelegateName = $CheckDelegate.Name
                $DelegateEmail = $CheckDelegate.PrimarySmtpAddress
    
                $obj=new-object System.Object
    
                $obj|add-member -membertype NoteProperty -name "ObjectGuid" -value "."
    
                $obj|add-member -membertype NoteProperty -name "SamAccountName" -value "."
    
                $obj|add-member -membertype NoteProperty -name "UserPrincipalName" -value "."
    
                $obj|add-member -membertype NoteProperty -name "DisplayName" -value "."
    
                $obj|add-member -membertype NoteProperty -name "Company" -value "."
    
                $obj|add-member -membertype NoteProperty -name "Department" -value "."
    
                $obj|add-member -membertype NoteProperty -name "JobTitle" -value "."
                               
                $obj|add-member -membertype NoteProperty -name "PrimarySmtpAddress" -value "."
    
                $obj|add-member -membertype NoteProperty -name "Database" -value "."
    
                $obj|add-member -membertype NoteProperty -name "ItemCount" -value "."
    
                $obj|add-member -membertype NoteProperty -name "TotalItemSize" -value "."
    
                $obj|add-member -membertype NoteProperty -name "ArchiveItemCount" -value "."
    
                $obj|add-member -membertype NoteProperty -name "ArchiveTotalItemSize" -value "."
    
                $obj|add-member -membertype NoteProperty -name "CombinedTotalSize" -value "."
    
                $obj|add-member -membertype NoteProperty -name "EA1" -value "."
    
                $obj|add-member -membertype NoteProperty -name "EA2" -value "."
    
                $obj|add-member -membertype NoteProperty -name "EA3" -value "."
    
                $obj|add-member -membertype NoteProperty -name "EA4" -value "."
    
                $obj|add-member -membertype NoteProperty -name "EA5" -value "."
    
                $obj|add-member -membertype NoteProperty -name "LastLogonDate" -value "."
    
                $obj|add-member -membertype NoteProperty -name "LogonCount" -value "."
    
                $obj|add-member -membertype NoteProperty -name "RecipientType" -value "."
    
                $obj|add-member -membertype NoteProperty -name "RecipientTypeDetails" -value "."
                
                $obj|add-member -membertype NoteProperty -name "DelegateName" -value $DelegateName
    
                $obj|add-member -membertype NoteProperty -name "DelegateEmail" -value $DelegateEmail
    
                $obj|add-member -membertype NoteProperty -name "DelegateAccess" -value $DelegateAccess
     
                $Reportx+=$obj
    
                #$Reportx|export-csv c:\O365Reports\$report.csv -Append
                
                }
    
            If ($CheckDelegate.RecipientType -like "Mail*" -and $CheckDelegate.RecipientType -like "*Group" -and $ExpandDistributionGroups -eq $true) {
                Write-Log "ALERT: Expand distribution group membership. [$($CheckDelegate.Name)]"
                ForEach ($Member in Get-DistributionGroupMember $CheckDelegate.Name -ResultSize Unlimited) {
                    $CheckMember = Get-Recipient $Member -ErrorAction SilentlyContinue
                    If ($CheckMember -ne $null) {
                        $DelegateName = $DelegateID + ":" + $CheckMember.Name
                        $DelegateEmail = $CheckMember.PrimarySmtpAddress
                        $hash.Add("DelegateName", $($DelegateName))
                        $hash.Add("DelegateEmail", $($DelegateEmail))
                        $hash.Add("DelegateAccess", $($DelegateAccess))
                        #"$MailboxName,$MailboxEmail,$DelegateName,$DelegateEmail,$DelegateAccess" | Out-File $ExportFile -Append 
                        } } }
    
            If ($CheckDelegate.RecipientType -eq "Group" -and $ExpandSecurityGroups -eq $true) {
                Write-Log "ALERT: Expand security group membership. [$($CheckDelegate.Name)]"
                ForEach ($Member in (Get-Group $DelegateID)) {
                    $CheckMember = Get-Recipient $Member -ErrorAction SilentlyContinue
                    If ($CheckMember -ne $null) {
                        $DelegateName = $DelegateID + ":" + $CheckMember.Name
                        $DelegateEmail = $CheckMember.PrimarySmtpAddress
                        $hash.Add("DelegateName", $($DelegateName))
                        $hash.Add("DelegateEmail", $($DelegateEmail))
                        $hash.Add("DelegateAccess", $($DelegateAccess))
                        #"$MailboxName,$MailboxEmail,$DelegateName,$DelegateEmail,$DelegateAccess" | Out-File $ExportFile -Append
                        } } } }
     }
    
    #==========================Script Body==========================#
    
    Connect-OnPremiseExchange
    
    Get-Mailbox -ResultSize unlimited |?{!($_.DisplayName -like "*Discovery S*")} | foreach-object{ #For Loop 001
    
        Try{ #Try 001
    
            # --- Export mailbox access permissions
    
            If ($IncludeMailboxAccess -eq $true) {
                Write-Log "AUDIT: Mailbox access permissions..."
                $Delegates = @()
                $Delegates = (Get-MailboxPermission $_.DistinguishedName | Where { $DelegatesToSkip -notcontains $_.User -and $_.IsInherited -eq $false })
                    If ($Delegates -ne $null) {
                        ForEach ($Delegate in $Delegates) {
                            $DelegateAccess = $Delegate.AccessRights
                            Check-Delegates $Delegate.User} } }
    
            if($_.guid -ne $null){ #If 001 (Placeholder for any other filters as guid will always be there if its gotten this far)
    
                #*** Gather Mailbox Attributes ***
    
                $ObjectGuid=$_.guid
    
                $SamAccountName=$_.SamAccountName
    
                $DisplayName=$_.DisplayName
    
                $SmtpAddress=$_.PrimarySmtpAddress
    
                $Database=$_.database
    
                $UPN=$_.UserPrincipalName
    
                $RecipientType=$_.RecipientType
    
                $RecipientTypeDetails=$_.RecipientTypeDetails
    
                #*** Gather Mailbox Statistics Attributes ***
    
                $ItemCount=(Get-MailboxStatistics -Identity $DisplayName).ItemCount
    
                #$TotalItemSize=(get-mailboxstatistics –identity $DisplayName).totalitemsize.value.ToMB()
    
                #Check if the mailbox has an archive & Gather stats if exists
    
                if(($_.ArchiveGuid -ne "00000000-0000-0000-0000-000000000000")){ #if 002
    
                    Try{ #Try 002
    
                        $ArchiveItemCount=(Get-MailboxStatistics -Identity $DisplayName -Archive -erroraction SilentlyContinue).ItemCount
    
                        #$ArchiveTotalItemSize=(get-mailboxstatistics –identity $DisplayName -Archive -erroraction SilentlyContinue).totalitemsize.value.ToMB()
    
                        } #End Try 002
    
                            catch{ #Catch for Try 002
    
                            "[ERROR FROM CATCH001]`t $($ObjectGuid), $($DisplayName) $($_.Exception.Message)`r`n" | Out-File $log -Append
    
                            } #End Catch for Try 002
    
                      } #End IF 002
    
                $CombinedTotal=$TotalItemSize+$ArchiveTotalItemSize
    
    
                #*** Gather AD Attributes ***
    
                Try{ #Try 003
    
                    $ADUser=Get-ADUser $_.guid -Properties *
                    $Department=$Aduser.department
                    $Company=$ADUser.company
                    $JobTitle=$aduser.title
                    $LastLogonDate=$Aduser.LastLogonDate
                    $LogonCount=$Aduser.LogonCount
                    $EA1=$Aduser.extensionAttribute1
                    $EA2=$Aduser.extensionAttribute2
                    $EA3=$Aduser.extensionAttribute3
                    $EA4=$Aduser.extensionAttribute4
                    $EA5=$Aduser.extensionAttribute5
          
                    } #End Try 003
    
                    Catch{ #Catch for Try 003
    
                        "[ERROR FROM CATCH002]`t $($ObjectGuid), $($DisplayName) $($_.Exception.Message)`r`n" | Out-File $log -Append
    
                        } #End Catch for Try 003
    
                #*** Building Report ***
    
                $obj=new-object System.Object
    
                $obj|add-member -membertype NoteProperty -name "ObjectGuid" -value $ObjectGuid
    
                $obj|add-member -membertype NoteProperty -name "SamAccountName" -value $SamAccountName
    
                $obj|add-member -membertype NoteProperty -name "UserPrincipalName" -value $UPN
    
                $obj|add-member -membertype NoteProperty -name "DisplayName" -value $DisplayName
    
                $obj|add-member -membertype NoteProperty -name "Company" -value $Company
    
                $obj|add-member -membertype NoteProperty -name "Department" -value $Department
    
                $obj|add-member -membertype NoteProperty -name "JobTitle" -value $JobTitle
                               
                $obj|add-member -membertype NoteProperty -name "PrimarySmtpAddress" -value $SmtpAddress
    
                $obj|add-member -membertype NoteProperty -name "Database" -value $Database
    
                $obj|add-member -membertype NoteProperty -name "ItemCount" -value $ItemCount
    
                $obj|add-member -membertype NoteProperty -name "TotalItemSize" -value $TotalItemSize
    
                $obj|add-member -membertype NoteProperty -name "ArchiveItemCount" -value $ArchiveItemCount
    
                $obj|add-member -membertype NoteProperty -name "ArchiveTotalItemSize" -value $ArchiveTotalItemSize
    
                $obj|add-member -membertype NoteProperty -name "CombinedTotalSize" -value $CombinedTotal
    
                $obj|add-member -membertype NoteProperty -name "EA1" -value $EA1
    
                $obj|add-member -membertype NoteProperty -name "EA2" -value $EA2
    
                $obj|add-member -membertype NoteProperty -name "EA3" -value $EA3
    
                $obj|add-member -membertype NoteProperty -name "EA4" -value $EA4
    
                $obj|add-member -membertype NoteProperty -name "EA5" -value $EA5
    
                $obj|add-member -membertype NoteProperty -name "LastLogonDate" -value $LastLogonDate
    
                $obj|add-member -membertype NoteProperty -name "LogonCount" -value $LogonCount
    
                $obj|add-member -membertype NoteProperty -name "RecipientType" -value $RecipientType
    
                $obj|add-member -membertype NoteProperty -name "RecipientTypeDetails" -value $RecipientTypeDetails
     
                $Reportx+=$obj
    
                } #End IF 001
    
                else{
                
                    "[ERROR FROM IF 001]`t GUID Was blank" | Out-File $log -Append
    
                    }
    
            } #End Try 001
    
                Catch { #Catch for Try 001
    
                    "[ERROR FROM CATCH 001]`t GUID: $($ObjectGuid), DisplayName: $($DisplayName) Message: $($_.Exception.Message)`r`n" | Out-File $log -Append
                    
                    } #End Catch for Try 001
    
    }#End For Loop 001
    
    $Reportx|export-csv c:\O365Reports\$report.csv -Append
    
  • #96771

    Don Jones
    Keymaster

    Be advised the BLOCKQUOTE won't format code. Use the PRE tags.

    What you're running into is called _scope_ (help about_scope), and you're kind of mis-using it, which is a common pitfall in PowerShell. Generally speaking, you need to pass in, via parameter, any input a function needs to work with. You then pass that out from the function by means of the pipeline. Functions are meant to be self contained. So, to use some pseudo-code:

    function MyFunctionA {
     $obj = Do-Something
     $obj = MyFunctionB -InputObject $obj
     # I now have the modified $obj back in MyFunctionA
     # $obj will have a NoteProperty named "Whatever"
    }
    
    function MyFunctionB {
     Param(
      [object]$InputObject
     )
     $InputObject | Add-Member -Mem Noteproperty -Nam Whatever -Val "Something"
     Write-Output $InputObject
    }
    

    This'll require you to re-think the flow of your code, I suspect, but it's how PowerShell is designed to work.

    Hope this helps a little.

  • #96792

    Stuart
    Participant

    Hi Don, Thanks for the reply. Noted about PRE tags.

    Well I think I am passing in via parameter to the function, so that should be best practice from what I've understood.

    In terms of passing it out in the correct way. So I should use a pipe, ok. But I need to get the contents of the variable $obj which was generated by the self contained function (based on the input parameter) into a variable that is global to code outside of the function.

    I know you already get that, but I am struggling a little.

    So in short, I need to use pipe lining to pass vars between functions. Ok, I think I got it. JUst means I need to create the other code into a function and then pass the output of Check-Delegate into the new function. Rather than just trying to pass it back to a bit of code in different scope (which doesn't work anyway). So functions talk to functions and get called at the end of the script.

    Sorry for the layman's terms, I'm not a coder 🙂

  • #96797

    Don Jones
    Keymaster

    You've pretty much got it. This:

     $obj = MyFunctionB -InputObject $obj
    

    Is using the pipeline. The original $obj is passed to MyFunctionB, which does something to it. The revised object is output to the pipeline (via Write-Output), and re-captured into the same $obj variable. It's actually exactly how Add-Member works.

    • #96810

      Stuart
      Participant

      Great, Thanks, I am trying to update my code to reflect that. Will let you know 🙂

  • #96819

    Stuart
    Participant

    When passing inputs to functions (from other functions), do they have to accord with the name of the specified parameters of that function?

    Check-Delegates $Delegate.User

    This calls the function Check-Delegate and passes it the contents of a variable, specifically the contents of the 'user' variable within the array name $Delegate.

    Then function itself looks like this

    Function Check-Delegates ([string]$DelegateID) {

    So I understand you can pass any information to the function. Whatever you pass to it, the function will then internally reference that as $DelegateID in this example. Am I correct so far?

    Since I realise I need to split up some of my code into Functions, that means I need to pass multiple variables between functions. So if I need to say pass 10 variables between one function and another, do I then need 10 parameters specified in the receiving function?

    Something like this:

    Function Build-Report {
    [CmdletBinding()]
    [Parameter(Mandatory=$true)]
        [String]$Input1,
        [Parameter(Mandatory=$true)]
        [String]$Input2,
        [Parameter(Mandatory=$true)]
        [String]$Input3,
        [Parameter(Mandatory=$true)]
        [String]$Input4,
        [Parameter(Mandatory=$true)]
        [String]$Input5)

    Then the sending function would looks something like:

     
    $_.Somevar1 = Build-Report -Input1 $input1
    $_.Somevar2 = Build-Report -Input1 $input2
    $_.Somevar3 = Build-Report -Input1 $input3
    $_.Somevar4 = Build-Report -Input1 $input4
    $_.Somevar5 = Build-Report -Input1 $input5
    

    I am guessing I have over complicated this.

    One parameter for each unique bit of information that I want to pass to it. Then the receiving function can create some kind of structure / report from the various inputs.

    Thanks

  • #96827

    Don Jones
    Keymaster

    You're basically correct. Assuming a function definition of:

    function Do-Thing {
     param(
      [string]$Name,
      [int]$Counter
     )
    }
    

    I could call:

    Do-Thing -Name $myvar1 -Counter $barney
    

    Or

    Do-Thing $fred $mabel
    

    And all would be well. In the last example, the contents of $fred would be inserted into $name within the function.

    So if I need to say pass 10 variables between one function and another, do I then need 10 parameters specified in the receiving function?

    Yup.

    • #96906

      Stuart
      Participant

      Thanks for clarifying, will give it a go

You must be logged in to reply to this topic.