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 :))
Related
I have written a little script which should be export the output as CSV.
Here is my script:
$Jobs = Get-VBRJob
foreach ($Job in $Jobs) {
$JobName = $Job.Name
$Objects = $Job.GetObjectsInJob()
$RestorePoints = Get-VBRRestorePoint -Backup $JobName
$Day = $Job.ScheduleOptions.OptionsDaily.DaysSrv
$RP = $RestorePoints.Count
$VM = $Objects.Name
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "JobName" -Value $JobName
$obj | Add-Member -MemberType NoteProperty -Name "Objects" -Value $Objects
$obj | Add-Member -MemberType NoteProperty -Name "RestorePoints" -Value $RestorePoints
$obj | Add-Member -MemberType NoteProperty -Name "Day" -Value $Day
$obj | Add-Member -MemberType NoteProperty -Name "VM" -Value $VM
}
$obj | Export-Csv $path -NoType
I read something about doing it via New-Object PSObject, so I tried this, but the CSV has only one line and returns the types of the attributes not the value. Only "JobName" and "VM" is working fine.
Can anyone help me to get the value of "Objects", "RestorePoints" and "Day" into CSV?
$obj contains only the object you just created, so after the loop completes, the variable holds the last object created in the loop, which is then exported to the CSV. A better approach would be outputting the created objects in the loop and collecting the loop output in a variable. I'd also recommend avoiding Add-Member unless you need to add members to an object that had been created elsewhere.
$obj = foreach ($Job in $Jobs) {
...
New-Object -Type PSObject -Property #{
'JobName' = $JobName
'Objects' = $Objects
'RestorePoints' = $RestorePoints
'Day' = $Day
'VM' = $VM
}
}
Also, if the values you're assigning to the new object's properties are objects themselves, PowerShell will export the string representation of those objects to the CSV, which usually is the full name of the object's class. If you want particular values in the output you probably need to expand those further (e.g. 'VM' = $VM.Name). What exactly you need to do there depends on the actual object, though, so I can't help much there without knowing more about the structure of the objects.
I am attempting to use a script provided by Thriggle See His Answer Here and am having some issues with it. It works almost flawlessly for what I am doing - EXCEPT - it doesn't export the Created By, Created Date, Modified By, and Modified Date.
Is there a way to add those fields into the script?
Here is his script:
$url = "$url"
$listName = "$list"
$path ="c:\ColumnsOfList.csv"
$web = Get-SPWeb $url
$list = $web.Lists.TryGetList($listName)
$fields = $list.ContentTypes | %{ $_.FieldLinks } | select Name, DisplayName
$items = #() #array to store objects representing list items
$list.items | %{
$item = $_;
$hash = #{}; #hash table to store field name-value pairs
$fields | %{ $hash[$_.DisplayName] = $item[$_.Name] };
$items += new-object psobject -Property $hash }
$items | Export-Csv -Path $path
You could try this one.
Here is my test code.
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
#Get the Web
$web = Get-SPWeb -identity "http://sp:12001"
#Get the Target List
$list = $web.Lists["OrderDetails"]
#Array to Hold Result - PSObjects
$ListItemCollection = #()
#Get All List items where Status is "In Progress"
$list.Items | foreach {
$ExportItem = New-Object PSObject
$ExportItem | Add-Member -MemberType NoteProperty -name "Title" -value $_["Title"]
$ExportItem | Add-Member -MemberType NoteProperty -Name "OrderDate" -value $_["OrderDate"]
$ExportItem | Add-Member -MemberType NoteProperty -name "CreatedBy" -value $_["Author"]
$ExportItem | Add-Member -MemberType NoteProperty -name "Created" -value $_["Created"]
#Add the object with property to an Array
$ListItemCollection += $ExportItem
}
#Export the result Array to CSV file
$ListItemCollection | Export-CSV "C:\Lee\ListData.csv" -NoTypeInformation
#Dispose the web Object
$web.Dispose()
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
I'm trying trying to get two properties from two separate commands and add them to a variable to be able to further evaluate.
I was told a custom object would work...
Clear-Host
Add-PSSnapin citrix* -ErrorAction SilentlyContinue
$DRSrvs = Get-XAServer drptsw00* | select -ExpandProperty servername
$hash = $null
$hash = #{}
foreach ($DR in $DRSrvs) {
$hash = New-Object PsObject -Property #{
servername = $DR
Logins = (Get-XALoadEvaluator -ServerName $DR).LoadEvaluatorName
}
}
A hashtable is for mapping (unique) keys to values. If you need to map different servernames to login names use a hashtable, otherwise use custom objects. Either way you need to handle the data structures correctly.
Hashtable:
$hash = #{}
foreach ($DR in $DRSrvs) {
$hash[$DR] = (Get-XALoadEvaluator -ServerName $DR).LoadEvaluatorName
}
Custom object list:
$list = foreach ($DR in $DRSrvs) {
New-Object PsObject -Property #{
servername = $DR
Logins = (Get-XALoadEvaluator -ServerName $DR).LoadEvaluatorName
}
}
Assigning something to a variable in a loop replaces the previous value in that variable with each iteration, leaving you with just the last value after the loop finishes.
I used this method and got a very clean output. Citrix SDK for Powershell if very funny and has lots of gotchas.
Clear-Host
Add-PSSnapin citrix* -ErrorAction SilentlyContinue
$OutputData = $null
$OutputData = #()
$Srvs = Get-XAServer Srv123* | Select-Object -ExpandProperty ServerName
$object = New-Object PSObject
Add-Member -InputObject $object -MemberType NoteProperty -Name Servername -Value ""
Add-Member -InputObject $object -MemberType NoteProperty -Name LoadEval -Value ""
foreach ($Srv in $Srvs) {
$servername= $Srv
$LoadEval = ((Get-XALoadEvaluator -ServerName $Srv).LoadEvaluatorName)
$appObject = New-Object System.Object
$appObject |
Add-Member -MemberType NoteProperty -Name "ServerName" -Value $servername -PassThru |
Add-Member -MemberType NoteProperty -Name "LoadEval" -Value $LoadEval
$outputData += $appObject
}
I use Powershell's custom-object command to hold data points. Custom-object creates just one object and assigns a variable to it. Can Powershell go one step further and create new classes from which objects can be made?
In the examples below, I store three pieces of data: a server name, a timestamp, and the minutes since an event occurred on the server.
When I was learning Powershell, I put all this into a two-dimensional array:
$record = #("Server","Timestamp","Minutes")
for ($j = 0; $j -lt 10; $j++){
$record += #("Server1","$(get-date)",$j)
sleep 60
}
$record | export-csv -path c:\record.csv -no type information
export-csv doesn't play well with arrays, so I started using a custom object:
$record = #()
for ($j = 0; $j -lt 10; $j++){
$r = New-Object -TypeName PSObject
$r | Add-Member -MemberType NoteProperty -Name Server -Value ""
$r | Add-Member -MemberType NoteProperty -Name Timesteamp -Value ""
$r | Add-Member -MemberType NoteProperty -Name Minutes -Value ""
$r.server = "Server1"
$r.timestamp = "$(get-date)"
$r.minutes = "$j"
$record += $r
sleep 60
}
$record | export-csv -path c:\record.csv -no type information
That's exports correctly, and dealing with object properties is easier than dealing with columns in a two-dimensional array.
But if I want to create several custom objects that aren't in an array, I have to write the custom-object code over and over again.
$server1 = New-Object -TypeName PSObject
$server1 | Add-Member -MemberType NoteProperty -Name Server -Value ""
$server1 | Add-Member -MemberType NoteProperty -Name Timesteamp -Value ""
$server2 = New-Object -TypeName PSObject
$server2 | Add-Member -MemberType NoteProperty -Name Server -Value ""
$server2 | Add-Member -MemberType NoteProperty -Name Timesteamp -Value ""
#ad nauseum
What if Powershell could design custom classes in addition to custom objects? Like OO programming languages do? Something like:
class record {
-MemberType NoteProperty -Name Server -Value ""
-MemberType NoteProperty -Name Timestamp -Value ""
-MemberType NoteProperty -Name Minutes -Value ""
}
$server1 = new-object -TypeName record
$server2 = new-object -TypeName record
$server3 = new-object -TypeName record
Is that possible in Powershell?
You can define classes in PowerShell.
Add-Type -Language CSharp #"
public class Record{
public System.DateTime TimeStamp;
public string Server;
public int Minutes;
}
"#;
$MyRecord = new-object Record;
$MyRecord.Server = "myserver";
$MyRecord.Timestamp = Get-Date;
$MyRecord.Minutes = 15;
You could use a function as a faux constructor for your custom objects. You wouldn't ever have to duplicate your code, and you could use flags to set your properties right from the function call. Here's an example:
Function New-Constructor
{
param
(
[string]$Name,
[DateTime]$TimeStamp = (Get-Date)
)
$server = New-Object -TypeName PSObject
$server | Add-Member -MemberType NoteProperty -Name Server -Value $Name
$server | Add-Member -MemberType NoteProperty -Name TimeStamp -Value $TimeStamp
# Calling "server" below outputs it, acting as a "return" value
$server
}
And some sample output:
PS C:\> New-Constructor -Name "MyServer"
Server TimeStamp
------ ---------
MyServer 9/9/2013 3:27:47 PM
PS C:\> $myServer = New-Constructor -Name "MyServer"
PS C:\> $myServer
Server TimeStamp
------ ---------
MyServer 9/9/2013 3:27:57 PM
PS C:\> $newServer = New-Constructor -Name "NS" -TimeStamp (Get-Date).AddDays(-1)
PS C:\> $newServer
Server TimeStamp
------ ---------
NS 9/8/2013 3:33:00 PM
You can do a whole ton of stuff with functions that is out of the scope of this question. Instead, check out about_functions_advanced.
Another option.
Properties
You can replace the '$null' value of the property message to have an initial value.
The Prop object is a hashtable of keys (properties) and values (initial values).
$messageClass = New-Object -TypeName PSObject -Prop #{ message = $null; }
Methods
$messageClass | Add-Member -MemberType ScriptMethod -Name "ShowMessage" -Value {
Try
{
Write-Host $this.message
}
Catch
{
Throw $_.Exception
}
}
Constructors
The code below describes a constructor. Polymorphism is achieved using [Parameter(Mandatory=$false)] to assert or not the provision of the specified parameter.
function MessageClass {
param([Parameter(Mandatory=$true)]
[String]$mandatoryMessage,
[Parameter(Mandatory=$false)]
[String]$optionalMessage)
$messageObj = $messageClass.psobject.copy()
if ($optionalMessage)
{
$messageObj.message = "$mandatoryMessage $optionalMessage!"
}
else
{
$messageObj.message = "$mandatoryMessage!"
}
$messageObj
}
The constructor can then be called like this:
$var1 = 'Hello'
$var2 = 'World'
$example1 = MessageClass -mandatoryMessage $var1
$example2 = MessageClass -mandatoryMessage $var1 -optionalMessage $var2
To show the text:
$example1.ShowMessage()
$example2.ShowMessage()
The results would be:
Hello!
Hello World!
For best performance I would do it like that:
Add-Type -TypeDefinition '
public class recordEntry {
public string server;
public System.DateTime timestamp;
public int minutes;
public recordEntry(string _server, System.DateTime _timestamp, int _minutes) {
server = _server;
timestamp = _timestamp;
minutes = _minutes;
}
}'
$record = [System.Collections.ArrayList]#()
$record = foreach ($j in 0..10){
[recordEntry]::new("Server1", [datetime]::Now, $j)
}
$record | export-csv -path c:\record.csv -NoTypeInformation