SCOrchDev-SharePoint.psm1

#requires -Version 3 -Modules SCOrchDev-Exception
add-type @"
        using System.Net;
        using System.Security.Cryptography.X509Certificates;
 
            public class IDontCarePolicy : ICertificatePolicy {
            public IDontCarePolicy() {}
            public bool CheckValidationResult(
                ServicePoint sPoint, X509Certificate cert,
                WebRequest wRequest, int certProb) {
                return true;
            }
        }
"@


<#
    .SYNOPSIS
        Runs a rest query and either uses a PSCredential or not
 
    .OUTPUTS
        Results of the rest query
 
    .PARAMETER Uri
        Specifies the Uniform Resource Identifier (URI) of the Internet resource to which the web request is sent. This parameter supports HTTP, HTTPS, FTP, and FILE values.
 
    .PARAMETER Method
        Specifies the method used for the web request. Valid values are Default, Delete, Get, Head, Merge, Options, Patch, Post, Put, and Trace.
 
    .PARAMETER Body
        Specifies the body of the request. The body is the content of the request that follows the headers. You can also pipe a body value to Invoke-RestMethod.
         
        The Body parameter can be used to specify a list of query parameters or specify the content of the response.
         
        When the input is a GET request, and the body is an IDictionary (typically, a hash table), the body is added to the URI as query parameters. For other request types (such as POST), the body is set as the value of the request body in the standard
        name=value format.
         
        When the body is a form, or it is the output of another Invoke-WebRequest call, Windows PowerShell sets the request content to the form fields.
         
        For example:
         
        $r = Invoke-WebRequest http://website.com/login.aspx
        $r.Forms[0].Name = "MyName"
        $r.Forms[0].Password = "MyPassword"
        Invoke-RestMethod http://website.com/service.aspx -Body $r
         
        - or -
         
        Invoke-RestMethod http://website.com/service.aspx -Body $r.Forms[0]
     
    .PARAMETER Headers
        Specifies the headers of the web request. Enter a hash table or dictionary.
         
        To set UserAgent headers, use the UserAgent parameter. You cannot use this parameter to specify UserAgent or cookie headers.
 
    .PARAMETER ContentType
        Specifies the content type of the web request.
         
        If this parameter is omitted and the request method is POST, Invoke-RestMethod sets the content type to "application/x-www-form-urlencoded". Otherwise, the content type is not specified in the call.
 
    .PARAMETER Credential
        The PSCredential to use for the query. If not passed used default credentials
#>

Function Invoke-RestMethod-Wrapped
{
    Param(
        [Parameter(Mandatory = $True) ][string] $Uri,
        [Parameter(Mandatory = $False)][Microsoft.PowerShell.Commands.WebRequestMethod] $Method,
        [Parameter(Mandatory = $False)][object] $Body,
        [Parameter(Mandatory = $False)][string] $ContentType,
        [Parameter(Mandatory = $False)][System.Collections.IDictionary] $Headers,
        [Parameter(Mandatory = $False)][pscredential] $Credential = $Null
    )
    
    $null = $(
        $RestMethodParameters  = @{ 'URI'         = $URI         }

        If ( $Body        ) { $RestMethodParameters += @{ 'Body'        = $Body        } }
        If ( $Method      ) { $RestMethodParameters += @{ 'Method'      = $Method      } }
        If ( $Headers     ) { $RestMethodParameters += @{ 'Headers'     = $Headers     } }
        If ( $ContentType ) { $RestMethodParameters += @{ 'ContentType' = $ContentType } }

        If ( $Credential  ) { $RestMethodParameters += @{ 'Credential' = $Credential } }
        Else                { $RestMethodParameters += @{ 'UseDefaultCredentials' = $True } }

        $Results = $null
        Write-Debug -Message (ConvertTo-Json -InputObject $RestMethodParameters)
        [System.Net.ServicePointManager]::CertificatePolicy = new-object IDontCarePolicy
        $Results = Invoke-RestMethod @RestMethodParameters -Verbose:$False
    )
    return $Results
}
<#
    .SYNOPSIS
        Creates a well formed Uri for a SharePoint site with or without
        a list and list filter
 
    .OUTPUTS
        [string] -URI of the SharePoint site to query
 
    .PARAMETER SPFarm
        The farm to generate the Uri for
        Ex: solutions.contoso.com
 
    .PARAMETER SPSite
        The Site to generate the Uri for
        Ex: gcloud
 
    .PARAMETER SPList
        The list to generate the Uri for
        Ex: solutions.contoso.com
 
    .PARAMETER SPList
        The filter query to use in the Uri
        Ex: StatusValue eq 'New'
     
    .PARAMETER UseSSL
        Use ssl (https) or not
        Default is true
 
    .Example Site
        Format-SPUri -SPFarm 'solutions.contoso.com' `
        -SPSite 'gcloud'
     
        https://solutions.contoso.com/Sites/gcloud/_vti_bin/listdata.svc
 
    .Example List
        Format-SPUri -SPFarm 'solutions.contoso.com' `
        -SPSite 'gcloud' `
        -SPList 'AddDiskToAVirtualServer'
         
        https://solutions.contoso.com/Sites/gcloud/_vti_bin/listdata.svc/AddDiskToAVirtualServer
 
    .Example List with Filter
        Format-SPUri -SPFarm 'solutions.contoso.com' `
        -SPSite 'gcloud' `
        -SPList 'AddDiskToAVirtualServer' `
        -SPFilter 'StatusValue eq Failed'
 
        https://solutions.contoso.com/Sites/gcloud/_vti_bin/listdata.svc/AddDiskToAVirtualServer?$filter=StatusValue eq Failed
 
    .Example List with Filter and non SSL
        Format-SPUri -SPFarm 'solutions.contoso.com' `
        -SPSite 'gcloud' `
        -SPList 'AddDiskToAVirtualServer' `
        -SPFilter 'StatusValue eq Failed' `
        -UseSsl $false
 
        http://solutions.contoso.com/Sites/gcloud/_vti_bin/listdata.svc/AddDiskToAVirtualServer?$filter=StatusValue eq Failed
#>

Function Format-SPUri
{
    Param ( 
        [Parameter(Mandatory = $true) ][string] $SPFarm,
        [Parameter(Mandatory = $true) ][string] $SPSite,
        [Parameter(Mandatory = $false)][string] $SPCollection = 'Sites',
        [Parameter(Mandatory = $false)][string] $SPList,
        [Parameter(Mandatory = $false)][string] $SPFilter,
        [Parameter(Mandatory = $false)][bool]   $UseSSl = $true
    )
    $null = $(
        if($UseSSl)
        {
            $SPUri = 'https'
        }
        else
        {
            $SPUri = 'http'
        }
        $SPUri = "$SPUri`://$SPFarm/$SPCollection/$SPSite/_vti_bin/listdata.svc"
        if($SPList) 
        {
            $SPUri = "$SPUri/$($SPList)"
            if($SPFilter)
            {
                $SPUri = "$SPUri`?`$filter=$SPFilter"
            }
        }
    )
    return $SPUri
}
<#
    .SYNOPSIS
        Get all SharePoint list names for the given SharePoint site
 
    .OUTPUTS
        Arraylist of strings representing all list names
 
    .PARAMETER SPUri
        The full uri to the sharepoint site
        Ex: https://solutions.contoso.com/Sites/GCloud
 
    .PARAMETER SPFarm
        The name of the sharepoint farm to query
        Ex: solutions.contoso.com
 
    .PARAMETER SPSite
        The name of the sharepoint site to query
        Ex: gcloud
 
    .PARAMETER UseSSL
        Use ssl (https) or not
        Default is true
 
    .PARAMETER Credential
        Optional PSCredential to use when querying sharepoint
 
#>

Function Get-SPList
{

    Param( [Parameter(ParameterSetName = 'ExplicitURI', Mandatory = $True)][string]$SPUri,
           
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $true) ][string] $SPFarm,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $true) ][string] $SPSite,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $False)][string] $SPCollection = 'Sites',
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $false)][bool]   $UseSSl = $true,
           
    [Parameter(Mandatory = $false)][PSCredential] $Credential )

    $null = $(
        if(-not $SPUri)
        {
            $SPUri = Format-SPUri -SPFarm $SPFarm -SPSite $SPSite -SPCollection $SPCollection -UseSSl $UseSSl
        }

        $returnLists = New-Object -TypeName System.Collections.ArrayList

        $ListService = Invoke-RestMethod-Wrapped -Uri $SPUri -Credential $Credential
        
        $Lists = $ListService.Service.Workspace.ChildNodes.Title
        Foreach($List in $Lists) { if($List) { $returnLists.Add($list) } }
    )
    return $returnLists
}
<#
    .SYNOPSIS
        Get SharePoint list item(s).
 
    .OUTPUTS
        Converts SharePoint list items to SPListItem objects
 
    .PARAMETER SPUri
        URI of the SharePoint list or list item or list item child item to query (optional)
 
    .PARAMETER SPFarm
        The name of the SharePoint farm to query. Used with SPSite, SPList and UseSSl to create SPUri
        Use this parameter set or specifiy SPUri directly
 
    .PARAMETER SPSite
        The name of the SharePoint site. Used with SPFarm, SPList and UseSSl to create SPUri
        Use this parameter set or specifiy SPUri directly
     
    .PARAMETER SPList
        The name of the SharePoint farm to query. Used with SPFarm, SPSite and UseSSl to create SPUri
        Use this parameter set or specifiy SPUri directly
 
    .PARAMETER UseSSl
        The name of the SharePoint site. Used with SPFarm, SPSite and SPList to create SPUri
        Use this parameter set or specifiy SPUri directly
        Default Value: True
        Action: Sets either a http or https prefix for the SPUri
 
    .PARAMETER Filter
        Filter definition
     
    .PARAMETER DownloadAttachment
        A boolean flag. If set to true attachments will be downloaded
 
    .PARAMETER AttachmentFormat
        What format the attachment should be in
        Default: Binary
        ASCII: Ascii formatting
 
    .PARAMETER Credential
        Credential with rights to query SharePoint list. If not used default credentials will be used
 
    .EXAMPLE
        Get all list items from a SharePoint list
 
        Get-SPListItem -SPURI $SPListURI -Credential $SPCred
 
        .EXAMPLE
        Get all list items from a SharePoint list
 
        Get-SPListItem -SPFarm $SPFarm -SPSite $SPSite -SPList $SPList -Credential $SPCred
 
        .EXAMPLE
        Get all list items from a SharePoint list that match the specified filter
     
        Sample filters
        $SPFilter = "StageValue eq 'Complete'"
        $SPFilter = "StageValue ne 'Complete' and AssignedId ne HiddenAssignedId"
        $SPFilter = "Enabled and Status ne 'Launching'" # 'Enabled' is a boolean column
        $DateString = $Date.ToString( "s" ) # Dates in filters need to be in this format
        $SPFilter = "Status eq 'Pending' and StartTime lt datetime'$DateString'"
        $SPFilter = "substringof('Recycle',Title)"
 
        Get-SPListItem -SPFarm $SPFarm -SPSite $SPSite -SPList $SPList -Credential $SPCred -Filter $SPFilter
 
    .EXAMPLE
        Expand a linked property such as a linked user
 
        Get-SPListItem -SPFarm $SPFarm -SPSite $SPSite -SPList $SPList -ExpandProperty CreatedBy
 
    .EXAMPLE
        Expand all linked properties
 
        Get-SPListItem -SPFarm $SPFarm -SPSite $SPSite -SPList $SPList -ExpandProperty *
#>

Function Get-SPListItem
{
    [CmdletBinding(DefaultParameterSetName = 'BuildURI')]
    Param( 
        [Parameter(ParameterSetName = 'ExplicitURI', Mandatory = $True)]
        [string]$SPUri,
           
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True) ]
        [string]$SPFarm,
           
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True) ]
        [string]$SPSite,
           
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True) ]
        [string]$SPList,
           
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $False)]
        [string]$SPCollection = 'Sites',
           
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $False)]
        [bool]$UseSsl = $True,
           
        [Parameter(Mandatory = $False)]
        [string]$Filter,

        [Parameter(Mandatory = $False)]
        [string[]]$ExpandProperty,
           
        [Parameter(Mandatory = $False)]
        [bool] $DownloadAttachment = $False,
           
        [ValidateSet('ASCII','')]
        [Parameter(Mandatory = $False)]
        [string]$AttachmentFormat,
           
        [Parameter(Mandatory = $False)][PSCredential] $Credential = $Null
    )
    
    $null = $(
        if( -not $SPUri )
        {
            $SPUri = Format-SPUri -SPFarm       $SPFarm `
                                  -SPSite       $SPSite `
                                  -SPList       $SPList `
                                  -SPCollection $SPCollection `
                                  -UseSSl       $UseSsl
        }

        if ( $Filter ) { $SPUri += "?`$filter=$($Filter)" }
    
        # Get the first page of items in the list (up to the SQL defined limit, usually 1000)
        $MoreItems = Invoke-RestMethod-Wrapped -Uri $SPUri -Credential $Credential
        $RawList = @()

        # As long as we keep getting more items...
        while ( $MoreItems )
        {
            # Add the items to the list
            $RawList += $MoreItems

            # Get the next page of item in the list
            if($RawList[-1] -is [System.Xml.XmlElement]) 
            { 
                $LastID = $RawList[-1].Content.Properties.ID.'#text'
                if(-not [System.String]::IsNullOrEmpty($LastID))
                { 
                    If ( $Filter ) { $PageURI = "$SPUri&`$skiptoken=$LastID"  }
                    Else           { $PageURI = "$SPUri/?`$skiptoken=$LastID" }
            
                    $MoreItems = Invoke-RestMethod-Wrapped -Uri $PageURI -Credential $Credential
                }
                else
                {
                    break
                }
            }
            else
            {
                break
            }
        }
    
        $ReturnList = New-Object -TypeName 'System.Collections.Generic.List[Object]'

        foreach( $ListItem in $RawList )
        {
            $SPListItem = ConvertFrom-RawSPItem -ListItem $ListItem -Immutable $False
            
            if($ExpandProperty -contains '*')
            {
                if($ListItem -is [System.Xml.XmlElement]) { $links = $ListItem.      Link }
                else                                      { $links = $ListItem.Entry.Link }
                $ExpandLinks = ($links | Where-Object { $_.rel -ne 'edit' }).Title
            }
            else
            {
                $ExpandLinks = $ExpandProperty
            }
            foreach($Property in $ExpandLinks)
            {
                if($Property -eq 'Attachments')
                {
                    if($DownloadAttachment)
                    {
                        $LinkedItem = Get-SPListItemAttachment -SPUri $SPListItem.Id `
                                                               -Credential $Credential `
                                                               -AttachmentFormat $AttachmentFormat
                    }
                }
                else
                {
                    $LinkedItem = Get-SPListItemImmutable -SPUri "$($SPListItem.Id)/$Property" -Credential $Credential
                }
                if(-not ($SPListItem.LinkedItems.ContainsKey($Property)))
                {
                    $SPListItem.addLinkedItem($Property, $LinkedItem)
                }
            }
            $ReturnList.Add($SPListItem)
        }
    )
    return $ReturnList
}
<#
    .SYNOPSIS
        Get SharePoint list items that are immutable.
        This function is an internal function used by Get-SPListItem to return immutable
        objects that are linked to the target object
 
    .OUTPUTS
        Converts SharePoint list items to SPListItemImmutable
 
    .PARAMETER SPUri
        URI of the SharePoint list item
 
    .PARAMETER Credential
        Credential with rights to query SharePoint list. If not used default credentials will be used
#>

Function Get-SPListItemImmutable
{
    Param( [Parameter(Mandatory = $True) ][string]$SPUri,
    [Parameter(Mandatory = $False)][PSCredential] $Credential = $Null )

    $null = $(
        $Item = Invoke-RestMethod-Wrapped -Uri $SPUri -Credential $Credential
        if($Item)
        {
            $SPItemImmutable = @()
            foreach($i in $Item)
            {
                $SPItemImmutable += ConvertFrom-RawSPItem -ListItem $i -Immutable $True
            }
        }
    )
    return $SPItemImmutable
}
<#
    .SYNOPSIS
        Creates a new SPListItem object.
 
    .OUTPUTS
        A PSObject representing a SharePoint list item.
 
    .PARAMETER Id
        Th Id of the list item (e.g. 'http://d.solutions.generalmills.com/Sites/gcloud/_vti_bin/listdata.svc/MyList(1)')
 
    .PARAMETER Created
        The date when the item was created
 
    .PARAMETER Modified
        The date when the item was modified
 
    .PARAMETER Version
        The version of the item
 
    .PARAMETER Properites
        A dictionary representing the list item's properties, including column values
 
    .PARAMETER LinkedItems
        A dictionary whose keys are strings and values are list item objects. For example,
        "CreatedBy" would point to the SharePoint person object who created the list item.
#>

Function New-SPListItemObject
{
    Param(
        [Parameter(Mandatory = $True)]  [String] $Id,
        [Parameter(Mandatory = $True)]  [System.DateTime] $Created,
        [Parameter(Mandatory = $True)]  [System.DateTime] $Modified,
        [Parameter(Mandatory = $False)] [String] $Version,
        [Parameter(Mandatory = $False)] [System.Collections.Generic.Dictionary[String, Object]] $Properties  = (New-Object -TypeName 'System.Collections.Generic.Dictionary[String, Object]'),
        [Parameter(Mandatory = $False)] [System.Collections.Generic.Dictionary[String, Object]] $LinkedItems = (New-Object -TypeName 'System.Collections.Generic.Dictionary[String, Object]'),
        [Parameter(Mandatory = $False)] [Switch] $Immutable
    )

    $DisplayId = [System.String]::Empty
    if($Id -and $Properties)
    {
        if($Properties.ContainsKey('Path') -and $Properties.ContainsKey('Id'))
        {
            $SiteParts  = $id.Split('/')
            if($SiteParts.Length -ge 2) 
            { 
                $SiteURI    = "$($SiteParts[0])//$($SiteParts[2])" 
                $DisplayId  = "$SiteURI$($Properties['Path'])/DispForm.aspx?ID=$($Properties['Id'])"
            }
            else
            {
                $DisplayId = $Id
            }
        }
    }
    $ListName = $null
    if($Id -match '/([^/]+)\(\d+\)$')
    {
        $ListName = $Matches[1]
    }
    $ASMXWebservicePath = Format-SPListASMXPath -ListItemID $Id
    $SPListItemProperties = Split-SPListItemId -SPListItemID $Id

    $ObjectProperties = @{
        'Id'                     = [String] $Id ;
        'DisplayId'              = [String] $DisplayId ;
        'ASMXWebserviceEndpoint' = [String] $ASMXWebservicePath ;
        'ListName'               = [String] $ListName ;
        'ListItemIndex'          = [String] $SPListItemProperties.SPListItemIndex ;
        'Site'                   = [String] $SPListItemProperties.SPSite ;
        'Collection'             = [String] $SPListItemProperties.SPCollection ;
        'Farm'                   = [String] $SPListItemProperties.SPFarm ;
        'Created'                = [System.DateTime] $Created ;
        'Modified'               = [System.DateTime] $Modified ;
        'Version'                = [String] $Version ;
        'Properties'             = $Properties ;
        'LinkedItems'            = @{} ;
    }
    $addLinkedItem = {
        Param($PropertyName, $Item)
        $this.LinkedItems[$PropertyName] = $Item
    }
    # TODO: Implement immutable object functionality
    $SPListItem = New-Object -TypeName 'PSObject' -Property $ObjectProperties
    Add-Member -InputObject $SPListItem -MemberType ScriptMethod -Name 'addLinkedItem' -Value $addLinkedItem
    Return $SPListItem
}
<#
    .SYNOPSIS
        Given the URI for a SharePoint list item, gets the attachments for the list item.
 
    .OUTPUTS
        A list of attachment objects.
 
    .PARAMETER SPUri
        URI of the SharePoint list or list item to get attachment(s) for.
     
    .PARAMETER AttachmentFormat
        What format the attachment should be in
        Default: Binary
        ASCII: Ascii formatting
#>

Function Get-SPListItemAttachment
{
    Param(
        [Parameter(ParameterSetName = 'ExplicitURI', Mandatory = $True)][string] $SPUri,
        [ValidateSet('ASCII','')]
        [Parameter(Mandatory = $False)][String]       $AttachmentFormat,
        [Parameter(Mandatory = $False)][PSCredential] $Credential = $Null
    )

    if($Credential)
    {
        $WebRequestParams = @{'Credential' = $Credential}
    }
    else
    {
        $WebRequestParams = @{'UseDefaultCredentials' = $True}
    }
    $AttachmentList = New-Object -TypeName 'System.Collections.ArrayList'
    $Attachment = Invoke-RestMethod-Wrapped -Uri "$SPUri/Attachments" -Credential $Credential
    foreach($_Attachment in $Attachment)
    {
        $AttachmentObject = New-Object -TypeName 'PSObject' -Property @{'Uri' = $_Attachment.content.src; 'Content' = $null}
        $content = (Invoke-WebRequest -Uri $AttachmentObject.Uri @WebRequestParams).Content
        Switch($AttachmentFormat)
        {
            'ASCII'
            {
                $Attachmentobject.content = [System.Text.ASCIIEncoding]::ASCII.GetString($Content)
            }
            default
            {
                $attachmentobject.content = $content
            }
        }
        $AttachmentList.Add($AttachmentObject) | Out-Null
    }
    return $AttachmentList
}
<#
    .SYNOPSIS
        Takes the raw output of Invoke-RestMethod and wrapps it into a custom class
        for SharePoint objects (either SPListItem or SPListItemImmutable depending on
        the value of Immutable flag)
 
    .OUTPUTS
        Converts the output of Invoke-RestMethod into SharePoint list items
 
    .PARAMETER ListItem
        The Xml output of Invoke-RestMethod for a SharePoint list item
     
    .PARAMETER Immutable
        A flag to determine if the outputed object will be immutable or not
#>

Function ConvertFrom-RawSPItem
{
    Param ( [Parameter(Mandatory = $True) ]       $ListItem,
    [Parameter(Mandatory = $False)][bool] $Immutable = $False )
    $null = $(
        if ( $ListItem -is [System.Xml.XmlElement] ) { $Id = $ListItem.      id; $ListItemProperties = $ListItem.      Content.Properties }
        else                                         { $Id = $ListItem.Entry.id; $ListItemProperties = $ListItem.Entry.Content.Properties }
        if($ListItemProperties -as [bool])
        {
            $ListItemPropertyNames = ( $ListItemProperties | Get-Member -MemberType Property ).Name

            if($ListItemPropertyNames.Contains('Created')) {  }
            else { $ListItemCreated = [System.DateTime]::MinValue }

            if($ListItemPropertyNames.Contains('Modified')) { $ListItemModified = [DateTime]$ListItemProperties.Modified.'#text' }
            else { $ListItemModified = [System.DateTime]::MinValue }

            if($ListItemPropertyNames.Contains('Version')) { $ListItemVersion = $ListItemProperties.Version.'#text' }
            else { $ListItemVersion = [System.String]::Empty }

            $PropertyList = New-Object -TypeName 'System.Collections.Generic.Dictionary[string,object]'
            $ListItemCreated  = [System.DateTime]::MinValue
            $ListItemModified = [System.DateTime]::MinValue
            $ListItemVersion  = [System.String]::Empty

            foreach( $PropertyName in $ListItemPropertyNames )
            {
                $Property = $ListItemProperties."$PropertyName"
                if    ( $Property -is [string]    ) { $Value = [string]$Property }
                elseif( $Property.Null -eq 'True' ) { $Value = [string]''        }
                else
                {
                    switch -CaseSensitive ( $Property.Type )
                    {
                        'Edm.DateTime'       { $Value = [datetime]$Property.'#text' }
                        'Edm.DateTimeOffset' { $Value = [datetime]$Property.'#text' }
                        'Edm.Time'           { $Value = [timespan]$Property.'#text' }
                        'Edm.Int16'          { $Value = [int16]   $Property.'#text' }
                        'Edm.Int32'          { $Value = [int32]   $Property.'#text' }
                        'Edm.Int64'          { $Value = [int64]   $Property.'#text' }
                        'Edm.Decimal'        { $Value = [decimal] $Property.'#text' }
                        'Edm.Float'          { $Value = [single]  $Property.'#text' }
                        'Edm.Double'         { $Value = [double]  $Property.'#text' }
                        'Edm.Boolean'        { $Value = [boolean]($Property.'#text' -eq 'true') }
                        'Edm.Byte'           { $Value = [byte]    $Property.'#text' }
                        'Edm.SByte'          { $Value = [sbyte]   $Property.'#text' }
                        'Edm.Guid'           { $Value = [guid]    $Property.'#text' }
                        default              { $Value = [string]  $Property.'#text' }
                    }
                }
   
                $LowerCasePropertyName = $PropertyName.ToLower()
                Switch -CaseSensitive ( $LowerCasePropertyName )
                {
                    'created' 
                    {
                        $ListItemCreated = $Value   
                    }
                    'modified' 
                    {
                        $ListItemModified = $Value
                    }
                    'version' 
                    {
                        $ListItemVersion = $Value
                    }
                    default
                    {
                        $PropertyList.Add($PropertyName,$Value)
                    }
                }
            }
            $SPListItem = New-SPListItemObject -Id $Id -Created $ListItemCreated -Modified $ListItemModified `
            -Version $ListItemVersion -Properties $PropertyList -Immutable:$Immutable
        }
        else
        {
            Throw-Exception -Type 'NoPropertiesFound' `
                            -Message 'The returned SP List item had no properties' `
                            -Property @{
                                'ListItem' = $ListItem
                            }
        }
    )
    return $SPListItem
}
<#
    .SYNOPSIS
        Update SharePoint list item
 
    .OUTPUTS
        None
 
    .PARAMETER SPUri
        URI of the SharePoint list or list item or list item child item to update
 
    .PARAMETER SPFarm
        The name of the SharePoint farm to query. Used with SPSite, SPList, UseSSl and SPListItemIndex to create SPUri
        Use this parameter set or specifiy SPUri directly
 
    .PARAMETER SPSite
        The name of the SharePoint site. Used with SPFarm, SPList, UseSSl and SPListItemIndex to create SPUri
        Use this parameter set or specifiy SPUri directly
     
    .PARAMETER SPList
        The name of the SharePoint farm to query. Used with SPFarm, SPSite, UseSSl and SPListItemIndex to create SPUri
        Use this parameter set or specifiy SPUri directly
 
    .PARAMETER UseSSl
        The name of the SharePoint site. Used with SPFarm, SPSite, SPList and SPListItemIndex to create SPUri
        Use this parameter set or specifiy SPUri directly
        Default Value: True
        Action: Sets either a http or https prefix for the SPUri
 
    .PARAMETER SPListItemIndex
        Index of a specific SharePoint list item to update. Used with SPFarm, SPSite, SPList and UseSSl to create SPUri
 
    .PARAMETER Data
        A hashtable representing the data in the SharePoint list item to be updated.
        Each key must match an internal SharePoint column name or column reference.
 
    .PARAMETER SPListItem
        The SPList item with all modifications made to it to update
     
    .PARAMETER PassThru
        Return an updated version of the SPListItem object or not
        Defaults to False
 
    .PARAMETER Credential
        Credential with rights to update SharePoint list
 
    .EXAMPLE
        Update SharePoint list item
 
        $Data = @{ 'LastRun' = (Get-Date); 'Status' = $FinalStatus }
        Update-SPListItem -SPFarm $SPFarm -SPSite $SPSite -SPList $SPList -SPListItemIndex 7 -Data $Data -Credential $SPCred
 
    .EXAMPLE
        Update SharePoint list item
 
        $SPUri = "http://q.spis.contoso.com/sites/EApps/Self_Service/_vti_bin/listdata.svc/GroomingSchedule(3)"
        $Data = @{ 'LastRun' = (Get-Date); 'Status' = $FinalStatus }
        Update-SPListItem -SPUri $SPUri -Data $Data -Credential $SPCred
 
    .EXAMPLE
        Update SharePoint list items assigned to the given team with the new team name
 
        $SPFilter = "Team eq 'WE-Apps'"
        $ListItems = Get-SPListItem -SPFarm $SPFarm -SPSIte $SPSite -SPList $SPList -Filter $SPFilter -Credential $SPCred
        Foreach($ListItem in $ListItems)
        {
            $ListItem.Properties.Team = 'Web Hosting'
            Update-SPListItem -SPListItem $ListItem -Credential $SPCred
        }
#>

Function Update-SPListItem
{
    Param( 
        [Parameter(ParameterSetName = 'ExplicitURI', Mandatory = $True)][string]$SPUri,
           
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True) ][string]$SPFarm,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True) ][string]$SPSite,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True) ][string]$SPList,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $False)][string]$SPCollection = 'Sites',
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True) ][string]$SPListItemIndex,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $False)][bool]  $UseSsl = $True,
           
        [Parameter(ParameterSetName = 'BuildURI',    Mandatory = $True)]
        [Parameter(ParameterSetName = 'ExplicitURI', Mandatory = $True)][HashTable]$Data,

        [Parameter(ParameterSetName = 'SPListItem', Mandatory = $True)][object]$SPListItem,

        [Parameter(Mandatory = $False)][bool] $PassThru = $False,
        [Parameter(Mandatory = $False)][PSCredential] $Credential = $Null
    )

    $null = $(
        If($SPListItem)
        {
            # If a whole item is passed in compare all of the properties. If there are any that don't match
            # the current list item add them to the data hashtable for processing

            # Set SPUri to the item id

            $SPUri = $SPListitem.Id

            $CurrentSPListItem = Get-SPListItem -SPUri $SPUri -Credential $Credential
            $Data = @{}
        
            Foreach($PropertyKey in $SPListItem.Properties.Keys)
            {
                if($CurrentSPListItem.Properties."$($PropertyKey)" -ne $SPListItem.Properties."$($PropertyKey)")
                {
                    $Data.Add($PropertyKey, $SPListItem.Properties."$($PropertyKey)")
                }  
            }
        }
        ElseIf($SPFarm)
        {
            $SPUri = "$(Format-SPUri -SPFarm $SPFarm -SPSite $SPSite -SPList $SPList -SPCollection $SPCollection -UseSSl $UseSsl)($SPListItemIndex)"
        }
        ElseIf($SPUri)
        {
            # SPUri was passed no processing to do
        }
        Else
        {
            Write-Error -Message "You must pass one of the following parameter sets`n
                                 -SPUri `$SPUri -Data `$Data
                                 -SPFarm `$SPFarm -SPSite `$SPSIte -SPList `$SPList -SPListItemIndex
                                 `$SPListItemIndex -Data `$Data`n
                                 -SPListItem `$SPListITem"

        }

        # Convert all datetime values in the Data hashtable to short strings
        $UpdateData = @{}

        ForEach($Key in $Data.Keys)
        {
            if($Data[$Key] -is [DateTime]) { $UpdateData.Add($Key, $Data[$Key].ToString('s')) }
            else { $UpdateData.Add($Key, $Data[$Key]) }
        }

        # Convert the Hashtable to a JSON format
        $RESTBody = ConvertTo-Json -InputObject $UpdateData

        # Update the item using merge
        $Invoke = Invoke-RestMethod-Wrapped -Method Merge `
                                            -Uri    $SPUri `
                                            -Body   $RESTBody `
                                            -ContentType 'application/json' `
                                            -Headers @{ 'If-Match' = '*' } `
                                            -Credential $Credential

        if($PassThru) { $returnItem = Get-SPListItem -SPUri $SPUri -Credential $Credential }
    )
    if($PassThru) { return $returnItem }
}
<#
    .SYNOPSIS
        Add new SharePoint list item(s)
 
    .OUTPUTS
        None
 
    .PARAMETER SPUri
        URI of the SharePoint list (optional)
 
    .PARAMETER SPFarm
        The name of the SharePoint farm to query. Used with SPSite, SPList and UseSSl to create SPUri
        Use this parameter set or specifiy SPUri directly
 
    .PARAMETER SPSite
        The name of the SharePoint site. Used with SPFarm, SPList and UseSSl to create SPUri
        Use this parameter set or specifiy SPUri directly
     
    .PARAMETER SPList
        The name of the SharePoint farm to query. Used with SPFarm, SPSite and UseSSl to create SPUri
        Use this parameter set or specifiy SPUri directly
 
    .PARAMETER UseSSl
        The name of the SharePoint site. Used with SPFarm, SPSite and SPList to create SPUri
        Use this parameter set or specifiy SPUri directly
        Default Value: True
        Action: Sets either a http or https prefix for the SPUri
 
    .PARAMETER Data
        A hashtable representing the data in the SharePoint list item to be created.
        Each key must match an internal SharePoint column name or column reference.
 
    .PARAMETER Credential
        Credential with rights to update SharePoint list
 
    .EXAMPLE
        Add SharePoint list item. Any unspecified columns take default values (if any).
 
    $Data = @{ 'LastRun' = (Get-Date); 'Status' = $FinalStatus }
        Add-SPListItem -SPFarm $SPFarm -SPSite $SPSite -SPList $SPList -Data $Data
#>

Function Add-SPListItem
{
    Param( 
        [Parameter(ParameterSetName = 'ExplicitURI', Mandatory = $True)][string]$SPUri,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True) ][string]$SPFarm,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True) ][string]$SPSite,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True) ][string]$SPList,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $False)][string]$SPCollection = 'Sites',
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $False)][bool]  $UseSsl = $True,
        [Parameter(Mandatory = $True) ][HashTable]    $Data,
        [Parameter(Mandatory = $False)][bool]         $PassThru = $False,
        [Parameter(Mandatory = $False)][PSCredential] $Credential = $Null
    )

    if(-not $SPUri)
    {
        $SPUri = Format-SPUri -SPFarm $SPFarm -SPSite $SPSite -SPCollection $SPCollection -SPList $SPList -UseSSl $UseSsl
    }
    $UpdateData = @{}

    ForEach($Key in $Data.Keys)
    {
        if($Data[$Key] -is [DateTime]) { $UpdateData.Add($Key, $Data[$Key].ToString('s')) }
        else { $UpdateData.Add($Key, $Data[$Key]) }
    }

    # Convert the Hashtable to a JSON format
    $RESTBody = ConvertTo-Json -InputObject $UpdateData

    $Invoke = Invoke-RestMethod-Wrapped -Method      Post `
                                        -URI         $SPUri `
                                        -Body        $RESTBody `
                                        -ContentType 'application/json' `
                                        -Credential  $Credential
}
<#
    .SYNOPSIS
        Delete target SharePoint list item
 
    .OUTPUTS
        None
 
    .PARAMETER SPUri
        URI of the SharePoint list item to delete
 
    .PARAMETER SPFarm
        The name of the SharePoint farm to delete. Used with SPSite, SPList, UseSSl and SPListItemIndex to create SPUri
        Use this parameter set or specifiy SPUri directly
 
    .PARAMETER SPSite
        The name of the SharePoint site. Used with SPFarm, SPList, UseSSl and SPListItemIndex to create SPUri
        Use this parameter set or specifiy SPUri directly
     
    .PARAMETER SPList
        The name of the SharePoint farm to query. Used with SPFarm, SPSite, UseSSl and SPListItemIndex to create SPUri
        Use this parameter set or specifiy SPUri directly
 
    .PARAMETER UseSSl
        The name of the SharePoint site. Used with SPFarm, SPSite, SPList and SPListItemIndex to create SPUri
        Use this parameter set or specifiy SPUri directly
        Default Value: True
        Action: Sets either a http or https prefix for the SPUri
 
    .PARAMETER SPListItemIndex
        Index of a specific SharePoint list item to delete
 
    .PARAMETER SPListItem
        The SPList item with all modifications made to it to delete
 
    .PARAMETER Credential
        Credential with rights to the SharePoint list
 
    .EXAMPLE
        Delete SharePoint list item
#>

Function Remove-SPListItem
{
    Param( 
        [Parameter(ParameterSetName = 'ExplicitURI', Mandatory = $True)][string]$SPUri,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True) ][string]$SPFarm,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True) ][string]$SPSite,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True) ][string]$SPList,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $False)][string]$SPCollection = 'Sites',
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True) ][string]$SPListItemIndex,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $False)][bool]  $UseSsl = $True,
        [Parameter(ParameterSetName = 'SPListItem', Mandatory = $True)][object]$SPListItem,
        [Parameter(Mandatory = $False)][PSCredential] $Credential = $Null
    )

    $null = $(
        If($SPListItem)
        {
            # If a whole item is passed in compare delete it based on its Id

            $SPUri = $SPListitem.Id
        }
        ElseIf($SPFarm)
        {
            # If the BuildURI Parameter set is passed created the correct SPUri
            $SPUri = "$(Format-SPUri -SPFarm $SPFarm -SPSite $SPSite -SPCollection $SPCollection -SPList $SPList -UseSSl $UseSsl)($SPListItemIndex)"
        }
        ElseIf($SPUri)
        {
            # SPUri was passed no processing to do
        }
        Else
        {
            Write-Error -Message "You must pass one of the following parameter sets`n
                                 -SPUri `$SPUri
                                 -SPFarm `$SPFarm -SPSite `$SPSIte -SPList `$SPList -SPListItemIndex
                                 `$SPListItemIndex`n
                                 -SPListItem `$SPListItem"

        }
        $Invoke = Invoke-RestMethod-Wrapped -Method      Delete `
                                            -URI         $SPUri `
                                            -ContentType 'application/json' `
                                            -Credential  $Credential
    )
}
<#
    .SYNOPSIS
        Given a SharePoint person, returns that person's e-mail address as listed in Active Directory.
 
    .DESCRIPTION
        SharePoint does not always return a full description of a person. One field that is always returned
        is Account, so we can use that to do a lookup against AD.
 
    .OUTPUTS
        A string containing an e-mail address
 
    .PARAMETER SharePointPerson
        A Sharepoint list item describing a person (i.e. a LinkedItem describing a person from Get-SPListItem)
 
    .EXAMPLE
        $ListItem = Get-SPListItem -SPUri $Uri -ExpandProperty 'CreatedBy'
        $Email = Get-SharePointPersonEmail -SharePointPerson $ListItem.LinkedItems.CreatedBy
#>

Function Get-SharePointPersonEmail
{
    Param([Parameter(Mandatory = $True)] $SharePointPerson)
    $Domain, $Username = $SharePointPerson.Properties.Account.Split('\')
    $ADUser = Get-ADUser -Identity $Username -Properties mail -Server $Domain
    Return $ADUser.mail
}
<#
    .SYNOPSIS
        Compares the ScheduledStartTime value of the target SP List item
        against the current time. If the time is outside of the bounds returns
        false else returns true
 
    .PARAMETER SPUri
        The Uri to the list item to test
 
    .PARAMETER Credential
        Optional. Credential to use for querying sharepoint
 
    .PARAMETER MaxSecondOffset
        The maximum number of seconds away from the list item's 'starttime' parameter
        before the function will return false
 
    .EXAMPLE
        Test-SPListItemStartTime -SPUri $NewRequestUri -MaximumSecondOffset 900
#>

Function Test-SPListItemStartTime
{
    [OutputType([bool])]
    Param(
        [Parameter(Mandatory = $True)]
        [string]
        $SPUri,

        [Parameter(Mandatory = $False)]
        [PSCredential]
        $Credential,

        [Parameter(Mandatory = $False)]
        [int]
        $MaxSecondOffset = 300
    )
    $null = $(
        $GetSPListItemParameters = @{
            'SPUri' = $SPUri
        }
     
        if($Credential) { $GetSPListItemParameters.Add('Credential', $Credential) }
        $ListItem = Get-SPListItem @GetSPListItemParameters
        
        if(-not $ListItem.Properties.ContainsKey('StartTime'))
        {
            Throw-Exception -Type 'StartTimeKeyMissing' `
                            -Message 'The list item passed does not have a StartTime property.' `
                            -Property @{
                'GetSPListItemParameters' = (ConvertTo-JSON -InputObject $GetSPListItemParameters) ;
                'ListItem' = (ConvertTo-JSON -InputObject $ListItem) ;
            }
        }

        $StartTime = $ListItem.Properties.StartTime -as [datetime]
        $Status = ($StartTime.AddSeconds($MaxSecondOffset) -ge (Get-Date)) -as [bool]
    )
    Return $Status
}
<#
    .Synopsis
        Adds an attachment to a SharePoint list item.
 
    .Paramter SPUri
        The URI to the list item to attach the item to
 
    .Paramter SPFarm
        The farm of the list item to attach the atachment to. Used for building
        the SPUri
 
    .Paramter SPSite
        The site of the list item to attach the atachment to. Used for building
        the SPUri
 
    .Paramter SPList
        The list of the list item to attach the atachment to. Used for building
        the SPUri
 
    .Paramter SPCollection
        The collection of the list item to attach the atachment to. Used for building
        the SPUri
 
    .Paramter SPListItemIndex
        The item index of the list item to attach the atachment to. Used for building
        the SPUri
 
    .Paramter UseSsl
        Use SSL (True or False) for building the SPUri
 
    .Paramter SPListItem
        An item pointer to the item to add the attachment to
 
    .Paramter Credential
        Optional credential to use for accessing the sharepoint list
 
    .Paramter AttachmentPath
        The path to the item to attach to the list
 
    .Example
        Add-SPListItemAttachment -SPListItem $SPListItem -Credential $SPCred -AttachmentPath 'C:\temp\blah.xlsx'
#>

Function Add-SPListItemAttachment
{
    Param(
        [Parameter(ParameterSetName = 'ExplicitURI', Mandatory = $True)]
        [string]
        $SPUri,
           
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True)]
        [string]
        $SPFarm,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True)]
        [string]
        $SPSite,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True)]
        [string]
        $SPList,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $False)]
        [string]
        $SPCollection = 'Sites',
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $True)]
        [string]
        $SPListItemIndex,
        [Parameter(ParameterSetName = 'BuildURI', Mandatory = $False)]
        [bool]
        $UseSsl = $True,
           
        [Parameter(ParameterSetName = 'SPListItem', Mandatory = $True)]
        [object]
        $SPListItem,

        [Parameter(Mandatory = $False)]
        [PSCredential]
        $Credential = $null,

        [Parameter(Mandatory = $True)]
        [string]
        $AttachmentPath
    )

    $null = $(
        If($SPUri)
        {
            # If a whole item is passed in compare delete it based on its Id
            $SPListItem = Get-SPListItem -SPUri $SPUri
        }
        ElseIf($SPFarm)
        {
            # If the BuildURI Parameter set is passed created the correct SPUri
            $SPUri = "$(Format-SPUri -SPFarm $SPFarm -SPSite $SPSite -SPCollection $SPCollection -SPList $SPList -UseSSl $UseSsl)($SPListItemIndex)"
            $SPListItem = Get-SPListItem -SPUri $SPUri
        }

        if(-not (Test-Path -Path $AttachmentPath -ErrorAction Stop))
        {
            Throw-Exception -Type 'FileNotFound' `
                            -Message 'Could not find the attachment file specified' `
                            -Property @{
                'AttachmentPath' = $AttachmentPath
            }
        }
        
        $AttachmentFI = Get-ChildItem -Path $AttachmentPath
        $FileBytes = Get-Content -Path $AttachmentPath `
                                 -Raw `
                                 -Encoding Byte
        
        $ASMXListItem = Get-ASMXListItem -SPListItem $SPListItem `
                                         -Credential $Credential
        $WebserviceProxy = New-SharepointASMXWebserviceProxy -SPListItem $SPListItem `
                                                             -Credential $Credential
        $null = $WebServiceProxy.AddAttachment(
            $ASMXListItem.Title -as [string],
            $SPListItem.ListItemIndex -as [string],
            $AttachmentFI.Name -as [string],
            $FileBytes -as [byte[]]
        )
    )
}
<#
#>

Function New-SharepointASMXWebserviceProxy
{
    Param(
        [Parameter(Mandatory = $True)]
        [object]
        $SPListItem,

        [Parameter(Mandatory = $False)]
        [PSCredential]
        $Credential = $Null
    )
    $Null = $(
        $WebServiceProxyParams = @{
            'Uri' = $SPListItem.ASMXWebserviceEndpoint
        }
        if($Credential) { $WebServiceProxyParams.Add( 'Credential', $Credential ) }
        else            { $WebServiceProxyParams.Add( 'UseDefaultCredential', $True ) }
        $WebServiceProxy = New-WebServiceProxy @WebServiceProxyParams
    )
    Return $WebServiceProxy
}
<#
#>

Function Get-ASMXListItem
{
    Param(
        [Parameter(Mandatory = $True)]
        [object]
        $SPListItem,

        [Parameter(Mandatory = $False)]
        [PSCredential]
        $Credential
    )
    $null = $(
        $WebServiceProxy = New-SharepointASMXWebserviceProxy -SPListItem $SPListItem `
                                                             -Credential $Credential
        $AllASMXLists = $WebServiceProxy.GetListCollection()
        for($i = 0 ; $i -lt $AllASMXLists.List.Count ; $i++)
        {
            $ASMXListItem = $AllASMXLists.List[$i]
            $FormattedListName = $ASMXListItem.Title.Replace(' ','')
            if($FormattedListName -eq $SPListItem.ListName)
            {
                break
            }
        }

        if($i -ge $AllASMXLists.List.Count)
        {
            Throw-Exception -Type 'ASMXListMatchNotFound' `
                            -Message 'Did not find a list in the ASMX webservice with the same target name' `
                            -Property @{
                                'ListName' = $SPListItem.ListName
                            }
        }
    )
    return $ASMXListItem
}
<#
    .Synopsis
        Takes a list item ID (path to the item for the rest interface) and converts it to the path to the
        asmx web service
 
    .Parameter ListItemID
        The list item ID to convert
#>

Function Format-SPListASMXPath
{
    [OutputType([string])]
    Param(
        [Parameter(Mandatory = $True)]
        [String]
        $ListItemID
    )

    $SPListASMXPath = "$($listitemid.Substring(0,$listitemid.ToLower().IndexOf('/listdata.svc')))/lists.asmx?WSDL"

    Return $SPListASMXPath -as [string]
}
Function Split-SPListItemId
{
    Param(
        [Parameter(Mandatory = $True)]
        [string]
        $SPListItemID
    )

    $Null = $(
        $SPListItemArray = $SPListItemID.Split('/')
        if($SPListItemArray.Length -ge 2) { $SPFarm = $SPListItemArray[2] }
        else { $SPFarm = '' }
        
        if($SPListItemArray.Length -ge 3) { $SPCollection = $SPListItemArray[3] }
        else { $SPCollection = '' }
        
        if($SPListItemArray.Length -ge 4) { $SPSite = $SPListItemArray[4] }
        else { $SPSite = '' }
        
        if($SPListItemArray.Length -ge 7) 
        { 
            $SPListObj = $SPListItemArray[7]
            $SPListObjArray = $SPListObj.Split('()')
            $SPList = $SPListObjArray[0]
            if($SPListObjArray.Length -ge 1)
            {
                $SPListItemIndex = $SPListObjArray[1]
            }
            else
            {
                $SPListItemIndex = ''
            }
        }
        else { $SPList = '' ; $SPListItemIndex = '' }
        
        $ObjectProperties = @{
            'SPFarm' = $SPFarm ;
            'SPCollection' = $SPCollection ;
            'SPSite' = $SPSite ;
            'SPList' = $SPList ;
            'SPListItemIndex' = $SPListItemIndex
        }
        $SPListItemProperties = New-Object -TypeName 'PSObject' -Property $ObjectProperties
    )
    return $SPListItemProperties
}
Export-ModuleMember -Function * -Verbose:$false