I needed to set up a number of Windows server VMs (Windows 2012R2) as a test bed for a vulnerability scanning suite. This would have been fast & easy using AWS EC2 instances (or Azure!), but I decided to use my internal VMWare infrastructure instead.
For CentOS VMs I would typically use one of three things to configure the static IP, gateway, and default nameservers:
nmtui (a text user interface to the network manager)
I created a Powershell script to set a few mailbox properties. I wanted to pipe in an array of mailbox objects, i.e. the results of a Get-Mailbox command, like so:
$Mailboxes | C:\Set-MailboxProperties.ps1
However, Set-MailboxProperties.ps1 only processed the first item in the array.
How do you see the full list? There are a couple ways:
Select -ExpandProperty Get-Mailbox chris | Select -ExpandProperty AddressListMembership
$FormatEnumerationLimit =-1
This is a per-session variable in PowerShell. By default the value is 4, but if you change it to -1 it will enumerate all items. This will affect every property of every object, so it may be more than you need.
By default, all users in the same Exchange Online environment can view each other’s free/busy time. Using the Organization–Sharing settings you can share more information, but not less.
Individuals can adjust their own free/busy time sharing in Outlook or Outlook Web App (OWA). But what if you have less-privileged users who should not be able to view another user’s free/busy time, for example, temporary employees or contract workers? Can they be restricted from viewing calendar information for other users?
It can be done, but it’s not simple.
My 3-part approach, summarized:
Change each user’s sharing settings for the Default user to None via PowerShell
Create a mail-enabled universal security group containing all privileged users. (Fortunately, this group already existed within my organization.)
Change each user’s sharing settings for the security group created above to AvailabilityOnly via PowerShell (to allow just Free/Busy visibility)
I found Add Calendar Permissions in Office 365 via Powershell, which was a tremendous help in discovering the format of the calendar folder. For example, to adjust the Default user’s access to chris@example.com’s calendar to None, use the following PowerShell command: Set-MailboxFolderPermission -Identity chris@example.com:\calendar -user Default -AccessRights None
Then I tried to add permissions for the security group: $mycal = 'chris@example.com:\calendar'
Set-MailboxFolderPermission -Identity $mycal -User privileged-users-security-group@example.com -AccessRights AvailabilityOnly
Error: There is no existing permission entry found for user: privileged-users-security-group.
+ CategoryInfo : NotSpecified: (:) [Set-MailboxFolderPermission], UserNotFoundInPermissionEntryException
+ FullyQualifiedErrorId : [Server=BLUPR0101MB1603,RequestId=d057882d-5663-417d-a614-ce73e5ab0565,TimeStamp=3/15/20
16 3:41:20 PM] [FailureCategory=Cmdlet-UserNotFoundInPermissionEntryException] B65CA2A0,Microsoft.Exchange.Managem
ent.StoreTasks.SetMailboxFolderPermission
+ PSComputerName : ps.outlook.com
Thanks to Setup secretary permissions to manage Calendar in Office 365, I discovered that the above error occurred because the security group had no current settings for the specified calendar. In that case, the Add-MailboxFolderPermission is the appropriate command:
Before running this across all of our users, I wanted to find out which users had customized their free/busy sharing settings. If they had customized them, I wanted to preserve their settings. For example, I decided to get the Default user sharing settings for the sales department users’ calendars:
Unfortunately, the above did not return all of the properties needed to identify the calendars in question: Calendar Default {AvailabilityOnly}
Calendar Default {LimitedDetails}
Calendar Default {AvailabilityOnly}
Calendar Default {AvailabilityOnly}
I specified a list of properties that was more useful: ForEach ($Mailbox In $DeptMailboxes) { $Calendar = $Mailbox.UserPrincipalName + ":\calendar"; Get-MailboxFolderPermission -Identity $Calendar -User Default | Select Identity,FolderName,User,AccessRights }
Fortunately, only a handful of the users in my organization had customized their sharing settings, so I simply noted their settings and re-applied them after running these settings across all users in the organization:
This achieved the desired free/busy time segmentation. However, there’s one snag: what happens when new users are added? They will have the default sharing settings. That means that every time a new user is added, these steps will need to be run for that new user. I created the following PowerShell script — I can pipe the results of Get-Mailbox to this script to apply the customizations described above:
To run the script (assuming it is named Set-CustomFreeBusySharing.ps1): Get-Mailbox -Identity bob@example.com | ./Set-CustomFreeBusySharing.ps1
Fully integrating that into my account creation process is a job for another day.
One other thing to note: users can still choose to modify their free/busy sharing with the Default user, in case they do want/need to share their availability with all users in the organization.
Other sites that had useful information while I researched this issue:
You can assign licenses only to user accounts that have the UsageLocation property set to a valid ISO 3166-1 alpha-2 country code. For example, US for the United States, and FR for France.
OK, so US for United States.
PowerShell Command Set-MsolUserLicense -UserPrincipalName "johndoe@example.com" -AddLicenses "exampletenant:EXCHANGESTANDARD_ALUMNI" -UsageLocation US
Error Set-MsolUserLicense : A parameter cannot be found that matches parameter name 'UsageLocation'.
Really, Microsoft? Is it a required parameter or is it not a parameter? Make up your minds!
Turns out, it’s a parameter of the Set-MsolUser cmdlet:
A common SharePoint request is to create a lookup field from a list in another site. This is possible so long as the sites are within the same site collection, but it does require some customization.
Since I am dealing with a single-server farm, I was able to do this fairly quickly in PowerShell:
# Set the site collection in which the source and destination webs exist
$site = Get-SPSite("https://mysite/path/")
# Set the source web to the web where the source list exists
$sourceweb = $site.OpenWeb("sourcepath")
# Set the destination web to the web where you wish to add the lookup
$destinationweb = $site.OpenWeb("destinationpath")
# Set the source list to the list in which the field exists
$sourcelist = $sourceweb.Lists["List Name"]
# Set the source field to the field you wish to perform lookups on
$sourcefield = $sourcelist.Fields["Field Name"]
# Get the collection of all fields in the destination list
$destinationfields = $destinationweb.Lists["List Name"].Fields
# Add a lookup field to the fields collection
# See the SPFieldCollection AddLookup(String,Guid,Guid,Boolean) method
# For details: http://msdn.microsoft.com/en-us/library/ms430950.aspx
$lookupname = $destinationfields.AddLookup($sourcefield.Title,$sourcelist.ID,$sourceweb.ID,false)
# The above produces an error: Missing expression after ','. At line:1, char:83
# Right--in Powershell, $false is False.
$lookupname = $destinationfields.AddLookup($sourcefield.Title,$sourcelist.ID,$sourceweb.ID,$false)
# Get the newly-added lookup field
$lookup = $destinationfields.GetFieldByInternalName($lookupname)
# Set the lookup field to the internal name of the source field
$lookup.LookupField = $sourcefield.InternalName
# Update the lookup -- without this the previous changes won't be saved
$lookup.Update()
# Dispose of the SPWeb and SPSite objects
$sourceweb.Dispose()
$destinationweb.Dispose()
$site.Dispose()
One of my colleagues created a PowerShell script that we use to migrate SharePoint 2010 sites from the SharePoint 2007 interface (UI 3) to the SharePoint 2010 interface (UI 4). The script works rather well for updating one or two sites at a time:
Today I received a request to update a list of 32 sites. After updating one, I thought–this is going to be tedious. We can improve this.
First, I updated the parameter to accept an array of strings instead of a single string.
Before param([string]$url = $(throw "Please specify a Site URL to convert to the SP2010 look and feel"))
After param([string[]]$url = $(throw "Please specify a Site URL to convert to the SP2010 look and feel"))
Next, I wrapped the call to the main function in a ForEach loop: ForEach ( $siteurl in $url ) {
...
# Throw in a line break to separate output
Write-Host ("`n`n")
}
The script can still take a single site as input (the string is treated as a string array with a single element), but now I can also pass it a list:
Earlier today I tried to update the page layout on a SharePoint 2010 site via the browser, which produced an error. I looked up the error’s correlation ID in the ULS logs and found this: GetFileFromUrl: ArgumentException when attempting get file Url https://oldsitename/webname/_catalogs/masterpage/Block.aspx Value does not fall within the expected range.
I couldn’t change the page layout via the browser. I couldn’t change the page layout via SharePoint Designer.
For whatever reason, I thought I could do it just as easily via PowerShell. I started following Jason Grimme’s post, Update SharePoint List Item(s) with Powershell, but I was unable to check-out/check-in the page or access the PublishingPageLayout property.
Liam Cleary’s reply on a TechNet post (Using PowerShell to Checkout and Checkin a file) tipped me off that I needed to use SPFile (rather than SPListItem) in order to perform the necessary operations. Here are the commands I ended up using: