Initialize-SPOCSOMObjectProperty.ps1

##############################
#.SYNOPSIS
#Initiaizes a specific properties on a CSOM object that was not originally returned.
#
#.DESCRIPTION
#Initialized specific properties on CSOM objects by creating lambda expressions to load into the context.
#
#.PARAMETER Objects
#The objects that contain the properties we want initialized or returned.
#
#.PARAMETER PropertyName
#The name of the properties we want returned.
#
#.PARAMETER Context
#The context these objects are from. This is used for bulk processing the commands.
#
#.PARAMETER BulkProcess
#This specified whether to attempt to return the properties in bulk. CSOM object become overloaded with more than 15 requests at a time. This can
#significantly reduce the execution time. However, it is unpredictable as wo what will cause a request overload. So this feature can be skipped using
#this switch.
#
#.EXAMPLE
#Initialize-SPOCSOMObjectProperty -Objects $Context.Web.Webs[0] -PropertyName "HasUniqueRoleAssignments" -BulkProcess -Context $Context
#
#Initialize-SPOCSOMObjectProperty -Objects $Context.Web.Webs -PropertyName @("HasUniqueRoleAssignments","MembersCanShare")
#
#.NOTES
#It would be nice to get the bulk process to work better. Some sort of load query management system or check.
##############################
Function Initialize-SPOCSOMObjectProperty() {
    [CmdletBinding()]
    param(

    #Provide the object(s) to be iterated trough
    [Parameter(Mandatory=$true,ValueFromPipeline,ParameterSetName="Object")]
    [Microsoft.SharePoint.Client.ClientObject[]]
    $Objects,
    
    #Supply the property names to generate the lambda expressions for CSoM
    [Parameter(Mandatory=$true,ParameterSetName="Object")]
    [String[]]
    $PropertyName,

    # Providing a context will allow for bulk processing
    [Parameter(Mandatory=$false,ParameterSetName="Object")]
    [Microsoft.SharePoint.Client.ClientContext]
    $Context,
    
    # Providing a context will allow for bulk processing
    [Parameter(Mandatory=$false,ParameterSetName="Object")]
    [Switch]
    $BulkProcess
    
    )
    Begin{

        #Check if all the objects have the same context. If so, speed of the process by assigning it to $context.
        if($objects.Count -gt 1){
            if((-not ($Objects | Where-Object {$_.Context -ne $Objects[0].Context})) -And (-Not $Context)){
                Write-Verbose -Message "No Context Supplied, but all objects are in the same context. Assigning to Context Variable."
                $context = $objects[0].Context
            }
        }
    }
    Process{

        #Iterate through each object supplied
        Write-Verbose -Message "Creating Lambda Expression for each property and object supplied."
        Foreach($Object in $Objects){

            Write-Progress -Activity "Initializing CSOM Collections" -PercentComplete (($Objects.IndexOf($Object) / $Objects.Length) * 100)
            $Expressions = @()
            #$load = [Microsoft.SharePoint.Client.ClientContext].GetMethod("Load")
            $type = $Object.GetType()
            
            Foreach($pn in $PropertyName){
                #Creates a ParamaterExpression node that can be used to identify a parameter or variable in an expression tree. Object Type and Object type name.
                $Parameter = [System.Linq.Expressions.Expression]::Parameter(($type), $type.Name)
                #Creates a member expression that represents accessing a property or field.
                $PropertyExpression = [System.Linq.Expressions.Expression]::PropertyOrField($Parameter,$pn)
                #Creates a unary expression that represents a type conversion
                $UnaryExpression = [System.Linq.Expressions.Expression]::Convert($PropertyExpression,[System.Object])
                #Creates a lambda expression by first constructing a delegate type.
                $Expression = [System.Linq.Expressions.Expression]::Lambda($UnaryExpression,$($Parameter))

                $Expressions += @($Expression)
            }

            #Load the object and lambda expressions to the Context.
            $Object.Context.Load($Object,$Expressions)

            #If no $context is set, execute the query against the Objects Context.
            if(-not $Context -or -not $BulkProcess){
                Write-Verbose -Message "Executing Single CSOM Query"
                $Object.Context.Load($Object,$Expressions)
                $object.Context.ExecuteQuery()
            }

            #If a conext was provided/derived, bulk process the requests. Process the requests 14 a time to prevent the request from using too many resources
            elseif(($objects.IndexOf($Object) % 12) -eq 0){
                Write-Verbose -Message "Reached resource limit for CSOM. Executing Query."
                $context.Load($object,$Expressions)
                $Context.ExecuteQuery()
            }
        }
    }
    End{
        If($Context){
            Write-Verbose -Message "Final Execute Query."
            $Context.ExecuteQuery()
        }
    }        
    
}