Problems with Variable (data not persistent)

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

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.

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 :slight_smile:

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.

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

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

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.

Thanks for clarifying, will give it a go