SharePoint Online CSOM/PowerShell Permissions - powershell

I have problems to list lists permissions using CSOM/PowerShell.
Variables / filters
$spSiteUrl = "https://mytenant.sharepoint.com"
Getting credentials
if($cred -eq $null)
{
$cred = Get-Credential
}
Loading Assemblies
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime") | Out-Null
Connecting into SharePoint and showing site title
Write-Host "Connecting to SharePoint"
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($spSiteUrl)
$ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($cred.UserName, $cred.Password)
$web = $ctx.Web
$ctx.Load($web)
$ctx.ExecuteQuery()
Write-host "Site Name : $($web.Title)"
Function listing "useful" applications
function getApps($web)
{
$appsArray = #()
$apps = $web.Lists
$ctx.Load($apps)
$ctx.ExecuteQuery()
Write-Host "List of aplications : "
foreach($app in $apps){
if($app.Hidden -eq $false)
{
$item = New-Object PSObject
$item | Add-Member -MemberType NoteProperty -Name 'Col1' -Value $($app.Title)
$item | Add-Member -MemberType NoteProperty -Name 'Col2' -Value $($app.HasUniqueRoleAssignments)
$item | Add-Member -MemberType NoteProperty -Name 'Col3' -Value $($app.RoleAssignments)
$item | Add-Member -MemberType NoteProperty -Name 'Col4' -Value $($app.BrowserFileHandling)
$item | Add-Member -MemberType NoteProperty -Name 'Col5' -Value $($app.EffectiveBasePermissions)
$item | Add-Member -MemberType NoteProperty -Name 'Col6' -Value $($app.Fields)
$item | Add-Member -MemberType NoteProperty -Name 'Col7' -Value $($app.WorkflowAssociations)
$appsArray += $item
}
}
$appsArray | Format-Table
}
Calling the function
getApps($web)
My problem is that :
$app.HasUniqueRoleAssignments
$app.RoleAssignments
$app.BrowserFileHandling
$app.EffectiveBasePermissions
$app.Fields
$app.WorkflowAssociations
Return me errors
The collection has not been initialized. It has not been requested or
the request has not been executed. It may need to be explicitly
requested..

The exception
The collection has not been initialized. It has not been requested or
the request has not been executed. It may need to be explicitly
requested.
Typically means that the property you are trying to work with (e.g. HasUniqueRoleAssignments) was not retrieved from the server yet.
You probably need an additional ExecuteQuery to load each app
foreach($app in $apps){
if($app.Hidden -eq $false)
{
$ctx.Load($app)
$ctx.ExecuteQuery()
You will eventually notice that some properties can't be retrieved with regular csom api (such as HasUniqueRoleAssignments) and for those you can use Gary's powershell, giving you the possibility to do what you otherwise would use linq
foreach($app in $apps){
if($app.Hidden -eq $false)
{
$ctx.Load($app)
Load-CSOMProperties -object $app -propertyNames #("HasUniqueRoleAssignments")
$ctx.ExecuteQuery()
https://gist.github.com/glapointe/cc75574a1d4a225f401b#file-load-csomproperties-ps1
https://sharepoint.stackexchange.com/questions/126221/spo-retrieve-hasuniqueroleassignements-property-using-powershell

Related

How to reference application settings (key vault references) in Azure Function using Powershell

I am writing a small program in Powershell which connects to Office 365 to download audit logs, make some changes and then export a CSV to an Azure Data Lake Storage account. To run this process on a schedule, I have created an Azure Function app (timer template) to run the program. To avoid hard-coded credentials, I created an Azure Key Vault to store the credential secrets. I created a managed identity in the Azure Function, created the secrets in Azure Key Vault with the credentials and then created three application settings in Azure Function under "Configuration" with the URL to point at the secrets stored in Azure Key Vault.
The three application settings are called "SecretUsername", "SecretPassword" (to point to the Office 365) and "SecretSAS" (to store the CSV in ADLS).
How do I refer to these variables in my Powershell script? I have tried different variations in my code, but none appear to work. Examples:
$uSecret = $SecretUsername
$uSecret = $ENV:SecretUsername
$uSecret = ENV:SecretUsername
$uSecret = (Get-ChildItem ENV:SecretUsername).SecretValueText
# Input bindings are passed in via param block.
param($Timer)
# Get the current universal time in the default string format.
$currentUTCtime = (Get-Date).ToUniversalTime()
# The 'IsPastDue' property is 'true' when the current function invocation is later than scheduled.
if ($Timer.IsPastDue) {
Write-Host "PowerShell timer is running late!"
}
# Write an information log with the current time.
Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime"
Set-ExecutionPolicy AllSigned
Set-Item ENV:\SuppressAzurePowerShellBreakingChangeWarnings "true"
$uSecret = (Get-ChildItem ENV:SecretUsername).SecretValueText
$pSecret = (Get-ChildItem ENV:SecretPassword).SecretValueText
$sasSecret = (Get-ChildItem ENV:SecretSAS).SecretValueText
$securePassword = ConvertTo-SecureString -String $pSecret -AsPlainText -Force
$UserCredential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $uSecret, $securePassword
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection
Import-PSSession $session
$startDate=(get-date).AddDays(-10)
$endDate=(get-date)
$scriptStart=(get-date)
$sessionName = (get-date -Format 'u')+'pbiauditlog'
$aggregateResults = #()
$i = 0 # Loop counter
Do {
$currentResults = Search-UnifiedAuditLog -StartDate $startDate -EndDate $enddate -SessionId $sessionName -SessionCommand ReturnLargeSet -ResultSize 1000 -RecordType PowerBIAudit
if ($currentResults.Count -gt 0) {
Write-Host ("Finished {3} search #{1}, {2} records: {0} min" -f [math]::Round((New-TimeSpan -Start $scriptStart).TotalMinutes,4), $i, $currentResults.Count, $user.UserPrincipalName )
# Accumulate the data.
$aggregateResults += $currentResults
# No need to do another query if the # records returned <1000 - should save around 5-10 seconds per user.
if ($currentResults.Count -lt 1000) {
$currentResults = #()
} else {
$i++
}
}
} Until ($currentResults.Count -eq 0) # End of Session Search Loop.
$data=#()
foreach ($auditlogitem in $aggregateResults) {
$datum = New-Object -TypeName PSObject
$d = ConvertFrom-json $auditlogitem.AuditData
$datum | Add-Member -MemberType NoteProperty -Name Id -Value $d.Id
$datum | Add-Member -MemberType NoteProperty -Name CreationTDateTime -Value $d.CreationDate
$datum | Add-Member -MemberType NoteProperty -Name CreationTime -Value $d.CreationTime
$datum | Add-Member -MemberType NoteProperty -Name RecordType -Value $d.RecordType
$datum | Add-Member -MemberType NoteProperty -Name Operation -Value $d.Operation
$datum | Add-Member -MemberType NoteProperty -Name OrganizationId -Value $d.OrganizationId
$datum | Add-Member -MemberType NoteProperty -Name UserType -Value $d.UserType
$datum | Add-Member -MemberType NoteProperty -Name UserKey -Value $d.UserKey
$datum | Add-Member -MemberType NoteProperty -Name Workload -Value $d.Workload
$datum | Add-Member -MemberType NoteProperty -Name UserId -Value $d.UserId
$datum | Add-Member -MemberType NoteProperty -Name ClientIPAddress -Value $d.ClientIPAddress
$datum | Add-Member -MemberType NoteProperty -Name UserAgent -Value $d.UserAgent
$datum | Add-Member -MemberType NoteProperty -Name Activity -Value $d.Activity
$datum | Add-Member -MemberType NoteProperty -Name ItemName -Value $d.ItemName
$datum | Add-Member -MemberType NoteProperty -Name WorkSpaceName -Value $d.WorkSpaceName
$datum | Add-Member -MemberType NoteProperty -Name DashboardName -Value $d.DashboardName
$datum | Add-Member -MemberType NoteProperty -Name DatasetName -Value $d.DatasetName
$datum | Add-Member -MemberType NoteProperty -Name ReportName -Value $d.ReportName
$datum | Add-Member -MemberType NoteProperty -Name WorkspaceId -Value $d.WorkspaceId
$datum | Add-Member -MemberType NoteProperty -Name ObjectId -Value $d.ObjectId
$datum | Add-Member -MemberType NoteProperty -Name DashboardId -Value $d.DashboardId
$datum | Add-Member -MemberType NoteProperty -Name DatasetId -Value $d.DatasetId
$datum | Add-Member -MemberType NoteProperty -Name ReportId -Value $d.ReportId
$datum | Add-Member -MemberType NoteProperty -Name OrgAppPermission -Value $d.OrgAppPermission
# Option to include the below JSON column however for large amounts of data it may be difficult for PBI to parse
$datum | Add-Member -MemberType NoteProperty -Name Datasets -Value (ConvertTo-Json $d.Datasets)
# Below is a simple PowerShell statement to grab one of the entries and place in the DatasetName if any exist
foreach ($dataset in $d.datasets) {
$datum.DatasetName = $dataset.DatasetName
$datum.DatasetId = $dataset.DatasetId
}
$data+=$datum
}
$dateTimestring = $startDate.ToString("yyyyMMdd") + "_" + (Get-Date -Format "yyyyMMdd") + "_" + (Get-Date -Format "HHmm")
$fileName = ($dateTimestring + ".csv")
Write-Host ("Writing to file {0}" -f $fileName)
$filePath = "$Env:temp/" + $fileName
$data | Export-csv -Path $filePath
Connect-AzAccount -Credential $UserCredential
Get-AzVM -ResourceGroupName "Audit" -status
$Context = New-AzStorageContext -StorageAccountName "auditingstorage" -StorageAccountKey $sasSecret
Set-AzStorageBlobContent -Force -Context $Context -Container "auditlogs" -File $filePath -Blob $filename
Remove-PSSession -Id $Session.Id
How do I reference the application settings in Azure Function so that I can use the stored secrets in my program?
Please assist! Many thanks!
To access the app settings, keyvault or not, you must retrieve it trhough : $env:APPSETTING_YourSettingName
Thus, for your keyvault referenced secret, you would access it through the following variables.
$env:APPSETTING_SecretUserName
$env:APPSETTING_SecretPassword
$env:APPSETTING_SecretSAS
And if ever you need to produce a list of them.
Get-ChildItem env:APPSETTING_*
Note, the values returned will plain text unencrypted string.
Therefore, in your code, this:
$uSecret = (Get-ChildItem ENV:SecretUsername).SecretValueText
becomes that:
$uSecret = $env:APPSETTING_SecretUserName
Additional note
Since it was pointed out in the comments, I'll mention it.
I am not advocating the use of clear text secret in app settings at all.
App settings should be a keyvault referene for any sensitive data.
I am simply stating that it can be retrieved within the function at runtime as clear-text through the $env:APPSETTING_YourSettingName variable.
Example:
AppSetting name : MySecretUser
AppSetting value: #Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/ec96f02080254f109c51a1f14cdb1931)
Actual secret value (In the keyvault) : I_AM_Secret
At runtime, getting the value of $env:APPSETTING_MySecretUser will return a String Object with the value I_AM_Secret

Issue with foreach loop (Combining Commands)

The script below works out great for identifying licensing for each individual host across multiple vCenters. What I am trying to include is the tag for each host as well. When I run the command individually it works fine, however when I run it as part of the code it is not functioning correctly. I highlighted the section if anyone can please take a look thanks. The line of code with the issue is commented out within the script below.
I attempted pushing this into a variable outside and insideof the foreach loop but I am receiving either 0 output, or the same output across each object.
Below is the actual command I put inside the foreach loop which is not functional.
(Get-VMhost | where{$_.Category -like "*Host*"})
$sw = [Diagnostics.Stopwatch]::StartNew()
# Declare our list of vCenters
[array]$vclistall = "vcenter01"
# Ensure were not connected to any vcenters
if ($DefaultVIServer.Count -gt 0) {
Disconnect-VIServer * -Confirm:$false -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Force:$true > $null
}
[array]$report = $null
foreach ($ScriptVCInstance in $vclistall) {
$connection = Connect-VIServer $ScriptVCInstance -ErrorAction SilentlyContinue
if ($connection) {
Write-Host "Collecting License Assets on vCenter $($ScriptVCInstance)"
# Get the license manager assets
$LicenseManager = Get-view LicenseManager
$LicenseAssignmentManager = Get-View $LicenseManager.LicenseAssignmentManager
$licenses = $LicenseAssignmentManager.GetType().GetMethod("QueryAssignedLicenses").Invoke($LicenseAssignmentManager, #($null))
#Format the asset into an object
foreach ($license in $Licenses) {
$object = New-Object -TypeName PSObject
$object | Add-Member -MemberType NoteProperty -Name "vCenter" -Value $($connection.name)
$object | Add-Member -MemberType NoteProperty -Name "Entity" -Value $($license.EntityDisplayName)
$object | Add-Member -MemberType NoteProperty -Name "Display Name" -Value $($license.Properties | where{$_.Key -eq 'ProductName'} | select -ExpandProperty Value)
$object | Add-Member -MemberType NoteProperty -Name "Product Version" -Calue $($License.Properties | where{$_.Key -eq 'FileVersion'} | select -ExpandProperty Value)
$object | Add-Member -MemberType NoteProperty -Name "License" -Value $($license.AssignedLicense.LicenseKey)
$object | Add-Member -MemberType NoteProperty -Name "License Name" -Value $($license.AssignedLicense.Name)
$object | Add-Member -MemberType NoteProperty -Name "Cost Unit" -Value $($license.Properties | where{$_.Key -eq 'CostUnit'} | select -ExpandProperty Value)
$object | Add-Member -MemberType NoteProperty -Name "Used License" -Value $($license.Properties | where{$_.Key -eq 'EntityCost'} | select -ExpandProperty Value)
$object | Add-Member -MemberType NoteProperty -Name "Total Licenses" -Value $($license.AssignedLicense.Total)
# Issue--> $object | Add-Member -MemberType NoteProperty -Name "Tag" -Value $(Get-VMhost | where{$_.Category -like "*Host*"})
$report += $object
if ($DefaultVIServer.Count -gt 0) {
Disconnect-VIServer * -Confirm:$false -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Force:$true > $null
}
} #end foreach $license
} else { # Else for if $connection
Write-warning "Not connected to vCenter $($ScriptVCInstance)"
} # endif $connection
} # End foreach $ScriptVCInstance
# write-out as a CSV file
Write-host "Exporting CSV $($env:USERPROFILE)\Licensed-Assets.csv"
$report | Sort-object "vCenter","License","Entity" | Export-csv "$($env:USERPROFILE)\Licensed-Assets.csv" -NoTypeInformation -UseCulture
$sw.Stop()
$sw.Elapsed

powershell script to return all forwarding rules in org

I need to pull all forwarding rules for an exchange online environment, and output them to a csv. this sounds simple, but I have an additional caveat. there are 23,000 mailboxes in the org.
I was able to write the script I needed, it outputted the data, but it timed out.
then I was able to break out only certain mailboxes that were critical (11,000) but I was still timing out in powershell.
so finally, I found an article that detailed breaking up a script into blocks of 1,000, and running numerous sessions. and runs! it runs without timing out.
but it doesn't output to the csv anymore.
since my script has gone through several iterations, I'm pretty sure that my problem is the way I'm storing, or outputting the array, but for all my staring at this, I cant figure it out. short of asking the doc for a prescription of Adderall, I figured id ask here. below is the offending script.
the aliaslist.csv that it mentions is just a csv with a list of aliases for 11,000 mailboxes. if you would like to run your own tests, you can adjust $pagesize down and paste a few mailboxes into a csv called aliaslist, stored in c:\temp
Function New-O365ExchangeSession()
{
param(
[parameter(mandatory=$true)]
$365master)
#close any old remote session
Get-PSSession | Remove-PSSession -Confirm:$false
#start a new office 365 remote session
$365session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://outlook.office365.com/powershell-liveid/" -Credential $365master -Authentication Basic -AllowRedirection
$office365 = Import-PSSession $365session
}
#set input variables
$path = "C:\temp"
$InputFile = aliaslist.csv"
$UserEmail = "admin#domain.com"
#set variables for csv usage
$Offset = 0;
$PageSize = 1000;
$MbxMax = (Import-Csv "$path/$InputFile").count
#Loop in the list and retrieve the device’s information
$file = “c:\temp\office365-$((get-date).tostring(“yyyy-MM-dd”)).csv”
$365master = get-credential $UserEmail
New-O365ExchangeSession $365master
# call the office365 remote connection function
do{
$mbxlist=#(import-csv "$path/$InputFile"|select-object -skip $Offset -First $PageSize)
"Process entry $($Offset) to $($Offset+$PageSize)"
#end csv input count reference
ForEach($mbx in $MbxList)
{
#Write to Host
"start Processing $($mbx.alias)"
#end Write to host,
#Check rules
$rules = Get-InboxRule -mailbox $_.alias | ? {$_.RedirectTo -ne $null -or $_.ForwardTo -ne $null -or $_.ForwardAsAttachmentTo -ne $null}
If ($rules -ne $null)
{
$rules | % {
#check for forwardAsAttachments
If ($_.ForwardAsAttachmentTo -ne $null)
{
$obj = New-Object system.object
$obj | Add-Member -name "NetID" -Value $_.alias -MemberType NoteProperty
$obj | Add-Member -name "ForwardType" -Value "Forward As Attachment Rule" -MemberType NoteProperty
$obj | Add-Member -name "ForwardAddress" -Value $_.forwardAsAttachmentTo -MemberType NoteProperty
$obj | Add-Member -name "Enabled" -Value $_.Enabled -MemberType NoteProperty
$obj | Add-Member -name "Description" -Value $f -MemberType NoteProperty
If (Test-Path $file)
{
$mbx.alias + ”,” + ($obj | ConvertTo-Csv)[2] | Out-File $file –Append
}
Else
{
$obj | Export-Csv $file -Encoding ASCII -notypeinformation
}
}
$obj = $null
#check for redirects
If ($_.redirectto -ne $null)
{
$obj = New-Object system.object
$obj | Add-Member -name "NetID" -Value $_.alias -MemberType NoteProperty
$obj | Add-Member -name "ForwardType" -Value "Redirct Rule" -MemberType NoteProperty
$obj | Add-Member -name "ForwardAddress" -Value $_.redirectto -MemberType NoteProperty
$obj | Add-Member -name "Enabled" -Value $_.Enabled -MemberType NoteProperty
$obj | Add-Member -name "Description" -Value $c -MemberType NoteProperty
If (Test-Path $file)
{
$mbx.alias + ”,” + ($obj | ConvertTo-Csv)[2] | Out-File $file –Append
}
Else
{
$obj | Export-Csv $file -Encoding ASCII -notypeinformation
}
}
$obj = $null
#check for forwards
If ($_.ForwardTo -ne $null)
{
$obj = New-Object system.object
$obj | Add-Member -name "NetID" -Value $_.alias -MemberType NoteProperty
$obj | Add-Member -name "ForwardType" -Value "Forward Rule" -MemberType NoteProperty
$obj | Add-Member -name "ForwardAddress" -Value $_.forwardto -MemberType NoteProperty
$obj | Add-Member -name "Enabled" -Value $_.Enabled -MemberType NoteProperty
$obj | Add-Member -name "Description" -Value $f -MemberType NoteProperty
If (Test-Path $file)
{
($obj | ConvertTo-Csv)[2] | Out-File $file –Append
}
Else
{
$obj | Export-Csv $file -Encoding ASCII -notypeinformation
}
}
$obj = $null
}
}
}
#increment the start point for the next chunk
$Offset+=$PageSize
#Call the office365 remote session function to close the current one and open a new session
New-O365ExchangeSession $365master
} while($Offset -lt $MbxMax)

EWS | Powershell: Load Calendar Item Extended Property

I'm trying to load set of extended properties into calendar appointment object in other to report and manipulate them. However, I've been having trouble with this. Whenever it gets to the line where it needs to load the extended properties, I get this error: "Exception calling "Load" with "1" argument(s): "Custom properties cannot be specified using property tags". The GUID and Id/Name combination must be used instead"
The line I'm having problems with is:
$apApointment.Load($psPropset);
The whole code is below. Any help is appreciated. By the way, I'm still a beginner with EWS. Thanks
Report = #()
$MailboxList = Read-Host "Enter path to txt file where users are saved."
$StartDate = Get-Date 1/1/2013
$EndDate = Get-Date 4/1/2013
#$StartDate = new-object System.DateTime(2014, 08, 27)
#$EndDate = new-object System.DateTime(2015, 02, 28)
#Logon to Exchange Web Service with default credentials
Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"
$sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
$user = [ADSI]"LDAP://<SID=$sid>"
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList ([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2)
$service.AutodiscoverUrl($user.Properties.mail)
Write-Progress -Activity "Preparing" -Status "Retrieving mailbox list" -PercentComplete 0
$Mailboxes = cat $MailboxList | get-mailbox
$Count = $Mailboxes.Count
#Go through each users found
ForEach ($Mailbox in $Mailboxes){
$DisplayName = $Mailbox.DisplayName
# $i = $i + 1
# $pct = $i/$Count * 100
# Write-Progress -Activity "Collecting mailbox details" -Status "Processing mailbox $i of $Count - $DisplayName" -PercentComplete $pct
Try {
$Ok = $true
$Mailbox = (Get-Mailbox $mailbox.WindowsEmailAddress -ErrorAction Stop ).PrimarySMTPAddress}
catch [System.Exception]{
$Ok = $false
}
if ($Ok){
#Set EWS up for impersonation of all users
$ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId -ArgumentList ([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress),$Mailbox
$service.ImpersonatedUserId = $ImpersonatedUserId
#Open user folder and bind calendar folder to the EWS service. Then, set each calendar default view to 1000
$folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar,$Mailbox)
$CalendarFolder = [Microsoft.Exchange.WebServices.Data.CalendarFolder]::Bind($service,$folderid)
$cvCalendarview = new-object Microsoft.Exchange.WebServices.Data.CalendarView($StartDate,$EndDate,1000)
#Query the calendar and return the appointments
$cvCalendarview.PropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
$frCalendarResult = $CalendarFolder.FindAppointments($cvCalendarview)
foreach ($apApointment in $frCalendarResult.Items){
#Go through each calendar items and collect thier attributes
$psPropset = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
#Create extended properties
$PR_SENT_REPRESENTING_NAME = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x42,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)
$PR_SENDER_NAME = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0xc1a,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)
$dispidApptTZDefStartDisplay = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x825E,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)
$dispidApptTZDefEndDisplay = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x825F,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)
$ptagSentRepresentingSimpleDispName = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x4031,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)
#Add extended properties to properties set
$psPropset.Add($PR_SENDER_NAME);
$psPropset.Add($PR_SENT_REPRESENTING_NAME);
$psPropset.Add($dispidApptTZDefStartDisplay);
$psPropset.Add($dispidApptTZDefEndDisplay);
$psPropset.Add($ptagSentRepresentingSimpleDispName);
#Add properties to calendar view
$cvCalendarview.PropertySet = $psPropset;
#Load properties into current appointment
**$apApointment.Load($psPropset);**
$SENDER_NAME = #()
$SENT_REPRESENTING_NAME = #()
$TZDefStartDisplay = #()
$TZDefEndDisplay = #()
$ptagSentRepSimpleName = #()
$apApointment.TryGetProperty($PR_SENT_REPRESENTING_NAME, [ref] $SENT_REPRESENTING_NAME)
$apApointment.TryGetProperty($PR_SENDER_NAME, [ref] $SENDER_NAME)
$apApointment.TryGetProperty($dispidApptTZDefStartDisplay, [ref] $TZDefStartDisplay)
$apApointment.TryGetProperty($dispidApptTZDefEndDisplay, [ref] $TZDefEndDisplay)
$apApointment.TryGetProperty($ptagSentRepresentingSimpleDispName, [ref] $ptagSentRepSimpleName)
$app = $apApointment.Subject
$start = $apApointment.Start
$End = $apApointment.End
$WhenCreated = $apApointment.DateTimeCreated
$Organizer = ($apApointment.Organizer).Address
$Required = $apApointment.RequiredAttendees.Count
$Recurring = $apApointment.IsRecurring
#Prepare objects needed for reports
$Obj = New-Object -TypeName PSObject
$Obj | Add-Member -MemberType NoteProperty -Name MeetingSubject -Value $app
$Obj | Add-Member -MemberType NoteProperty -Name MeetingStartTime -Value $start
$Obj | Add-Member -MemberType NoteProperty -Name MeetingEndTime -Value $End
$Obj | Add-Member -MemberType NoteProperty -Name WhenCreated -Value $WhenCreated
$Obj | Add-Member -MemberType NoteProperty -Name ReoccuringMeeting -Value $Recurring
$Obj | Add-Member -MemberType NoteProperty -Name MeetingOrganizer -Value $Organizer
$Obj | Add-Member -MemberType NoteProperty -Name SentRepresentingName -Value $SENT_REPRESENTING_NAME
$Obj | Add-Member -MemberType NoteProperty -Name SenderName -Value $SENDER_NAME
$Obj | Add-Member -MemberType NoteProperty -Name RequiredAttendeeCount -Value $Required
$Obj | Add-Member -MemberType NoteProperty -Name MailboxOwner -Value $Mailbox
$Obj | Add-Member -MemberType NoteProperty -Name MailboxOwnerDisplay -Value $DisplayName
$Report += $Obj
Write-Host "$Mailbox Calendar is being processed"
}
}
}
#Get all reports and save them into respective paths
$Report | Export-Csv c:\CorruptCalender.csv -NoTypeInformation
The two properties you define $dispidApptTZDefStartDisplay and $dispidApptTZDefEndDisplay are the problem. You've defined them using a constant property tag value that is in the 0x8000 range. MAPI tags in that range are named properties, and their actual tag values aren't set (they change from mailbox to mailbox). You need to define them in terms of a property set GUID and a property ID.
The values you have are actually property ID values, not tag values. So you need to combine them with a property set GUID to have them work. The managed API actually has a constant defined for the Appointment property set, which these properties belong to, so you can change those lines to:
$dispidApptTZDefStartDisplay = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Appointment, 0x825E,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)
$dispidApptTZDefEndDisplay = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Appointment, 0x825F,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)
And it should clear up that error (unless I borked my Powershell syntax :))

Add-Member in PowerShell Workflow

I have the following workflow in PowerShell:
workflow Audit-Computer
{
param([string[]]$Computers)
foreach -parallel ($computer in $Computers)
{
#Create a return object to hold the information we gather
$returnObject = [PSCustomObject]#{ComputerName=$computer}
#Test if the machine is reachable
$hostAlive = $true
try {
#Attempt a connection to the computer.
$os = Get-WmiObject –class Win32_OperatingSystem -PSComputerName $computer –erroraction Stop
#If we get here the connection was successful
$returnObject | Add-Member -MemberType NoteProperty -Name "HostAvailability" `
-Value "Online" -PassThru | `
Add-Member -MemberType NoteProperty -Name "OperatingSystem" -Value $os.Caption
} catch {
$hostAlive = $false
}
if($hostAlive){
#The host is alive lets run our audit
}
else
{
$returnObject | Add-Member -MemberType NoteProperty -Name "HostAvailability" -Value "Offline"
}
#Return the gathered information to the pipeline
Write-Output $returnObject
}
}
Unfortunately the Add-Member does not seem to work. The object returned does not contain the NoteProperties from add-member. How do I add properties to my custom object in a workflow?
There are several restrictions on cmdlets in PowerShell Workflows. According to this blog post, Add-Member is restricted to "local execution only".
I'm not able to test your script exactly, but this seemed to reproduce the same effect:
workflow Test-Workflow {
$obj = [PSCustomObject]#{ComputerName = 'test'}
$obj | Add-Member -MemberType NoteProperty -Name HostAvailable -Value "Online"
Write-Output $obj
}
but adding an InlineScript block made it work:
workflow Test-Workflow {
InlineScript {
$obj = [PSCustomObject]#{ComputerName = 'test'}
$obj | Add-Member -MemberType NoteProperty -Name HostAvailable -Value "Online"
Write-Output $obj
}
}