Manage Calendar Permissions in Office 365 PowerShell

I had been asked to update all users calendar permission to allow a certain application to be able to edit users calendar so it can automate adding days off and other calendar items.

Currently the only way this can be done that I know off is either using PowerShell or using MS Graph.

In this post we will be going through how to report calendar permission for all users and how to bulk update. We will be using the exchange online PowerShell module.

https://docs.microsoft.com/en-us/powershell/exchange/exchange-online-powershell-v2?view=exchange-ps#install-the-exo-v2-module

Install-Module -Name ExchangeOnlineManagement

To get the list of mailboxes and permissions we will use two command Get-Mailbox and Get-MailboxFolderPermission. Then we will be formatting the results for export out to a CSV.

To get calendar permission we will need to add :\Calendar to after the UserPrincipalName.

First step is that we need to connect to Exchange Online using PowerShell.

Connect-ExchangeOnline

To get a single user’s permission use

Get-MailboxFolderPermission -Identity user@domain.com:\calendar 

To check for a specific users permission we can add the -user parameter.

To get all users permission we need to get a list of all users using Get-Mailbox and then loop through each user to get the permission.

## Get all users mailboxes
$Mailboxes = Get-Mailbox -ResultSize Unlimited | Select-Object UserPrincipalName

## Loop through each mailbox
foreach ($mailbox in $Mailboxes) {

## Get default user calendar permissions
Write-Warning "Checking Permission on $($mailbox.UserPrincipalName)"
Get-MailboxFolderPermission -Identity "$($mailbox.UserPrincipalName):\Calendar" | 
Select-Object @{Name="UserPrincipalName";E={$mailbox.UserPrincipalName}},FolderName,User,AccessRights |
Export-Csv C:\temp\Office_365_Export\Calendar_Report\CalenarPermissionReport.csv -NoTypeInformation -Append
}

The export should look like the below.

Once I had a report of the current users calendar permission, I needed to add the user account that the application would use to add the calendar items.

To add calendar permission we use the Add-MailboxFolderPermission,

The roles that are available, along with the permissions that they assign are below.

  • Author: CreateItems, DeleteOwnedItems, EditOwnedItems, FolderVisible, ReadItems
  • Contributor: CreateItems, FolderVisible
  • Editor: CreateItems, DeleteAllItems, DeleteOwnedItems, EditAllItems, EditOwnedItems, FolderVisible, ReadItems
  • None: FolderVisible
  • NonEditingAuthor: CreateItems, FolderVisible, ReadItems
  • Owner: CreateItems, CreateSubfolders, DeleteAllItems, DeleteOwnedItems, EditAllItems, EditOwnedItems, FolderContact, FolderOwner, FolderVisible, ReadItems
  • PublishingEditor: CreateItems, CreateSubfolders, DeleteAllItems, DeleteOwnedItems, EditAllItems, EditOwnedItems, FolderVisible, ReadItems
  • PublishingAuthor: CreateItems, CreateSubfolders, DeleteOwnedItems, EditOwnedItems, FolderVisible, ReadItems
  • Reviewer: FolderVisible, ReadItems

To add a single user use the command below.

Add-MailboxFolderPermission -Identity user@domain.com:\calendar -User "Account to be added" -AccessRights Editor 

I also wanted to be able to run this script again and not have to modify users that already have the correct permission as this will return an error, so we will be using a if statement to only apply to users who don’t have the permission.

Below is the full script.

## Calendar Permission Variable
$Account = "Account to be added"
## Get all users mailboxes
$Mailboxes = Get-Mailbox -ResultSize Unlimited | Select-Object UserPrincipalName

## Loop through each mailbox
foreach ($mailbox in $Mailboxes) {

## Get default user calendar permissions
$permission = Get-MailboxFolderPermission -Identity "$($mailbox.UserPrincipalName):\Calendar"

## Report on Users without correct calendar permissions and set permissions
if ($permission.User.DisplayName  -notcontains $Account) {
Write-Warning "Calender permission not set on $($mailbox.UserPrincipalName) adding Permission"
Add-MailboxFolderPermission -Identity "$($mailbox.UserPrincipalName):\Calendar" -User $Account -AccessRights Editor | Out-Null
} 

## calendar permission are correct
else {Write-Host "Calender Permission are set on $($mailbox.UserPrincipalName)" -ForegroundColor Green}

}

Checking LAPS Password Cross-Forest Using PowerShell

We have been using LAPS for the last year or so and for the most part it is a great tool for managing local admins password.

In our environment we have multiple forest with trusts relationships and the one main issue that we have is that the LAPS UI client can’t check the local admin password of devices in other domains, unless we logon as a user in that domain and run the LAPS UI as that user.

We can use PowerShell but this requires either the commands to be run from a devices in the domain with the LAPS PowerShell module or can be done using remote ps session but this is not something everyone on the service desk would know how to do or will have rights to do.

Enter-PSSession dc.domain.local
Get-AdmPwdPassword -ComputerName "computer" | Select-Object Password,ExpirationTimestamp

We could also get the AD attributes by using get-adcomputer comandlet but this requires the AD PowerShell module.

Get-ADComputer -Identity "computer" -Server "DC" -properties ms-mcs-admpwd,ms-mcs-admpwdexpirationtime | select-object ms-mcs-admpwd,ms-mcs-admpwdexpirationtime

I wanted to try create a script that would allow them to check the other domains without the needing to know how to do PS remoting or having modules installed.

To get around using the AD module we will be using ADSI search instead as this a default part of PowerShell. I used the below blog to get started.

Use the PowerShell [adsiSearcher] Type Accelerator to Search Active Directory | Scripting Blog (microsoft.com)

Hey, Scripting Guy! How Can I Search Active Directory from Within Windows PowerShell? | Scripting Blog (microsoft.com)

To query the remote doamin using adsisearch we first need to have a way of converting the domain name to the format that adsisearch requires.

This can be done using DirectoryContect class

System.DirectoryServices.ActiveDirectory.DirectoryContext

System.DirectoryServices.ActiveDirectory.Domain

DirectoryContext Class (System.DirectoryServices.ActiveDirectory) | Microsoft Docs

The first part of the script gets the domain details and returns the objects back to the search variable.

Next part we will filter the results based on the client variable to return only the required computer details.

Once the script is working, I just needed to create some mandatory parameters and create a function called Check-LAPS so that it’s easier for people to use without having to hardcode anything in the script and has some help and examples.

Below is the link to the full script.

Scripts/ActiveDirectory/LAPS at master · TheSleepyAdmin/Scripts (github.com)

Below shows how the script is run and the results.

I used the above script with WPF to create a GUI that is now complied in to an exe. I though this would be a lot easier to pass on the service desk as all they need to do is run the exe.

This was compiled using ps2exe PowerShell module.

MScholtes/PS2EXE: Module to compile powershell scripts to executables (github.com)

All they need to do is put in the domain name and computer name.

This should then return the LAPS password.

I will do a different post on creating the GUI itself in as this was my first GUI and I would like to create a few other.

Exchange Online PowerShell Basic Authentication Connection Issue

Today I ran in to an issue where I couldn’t connect to Exchange Online PowerShell. I could connect to other service without issue (Office 365, Azure AD….).

When trying to connect to Exchange online I was getting an warning about basic auth.

At first I thought this was to do with Microsoft deprecating basic authentication

Exchange Online deprecating Basic Authentication – Microsoft Lifecycle | Microsoft Docs

but after reading the document it says that this doesn’t affect Exchange ActiveSync (EAS), IMAP, POP, and Remote PowerShell.

I then read the Exchange Online v2 module prerequisites

https://docs.microsoft.com/en-us/powershell/exchange/exchange-online-powershell-v2?view=exchange-ps#prerequisites-for-the-exo-v2-module

In this there is a part that winrm needs to be enabled for basic authentication even though the module uses modern authentication.

This had been working for a long time without issue so I didn’t know what exactly had changed so I decided to check the winrm config

To check the winrm configuration run the below command

winrm get winrm/config/client/auth

In my case it was being set by a GPO. We had recently applied the Windows 10 2004 baseline policy to all our Windows 10 devices so I decided to check the setting in that GPO.

Security baseline (FINAL): Windows 10 and Windows Server, version 2004 – Microsoft Tech Community

There is a spreadsheet in the document folder that has all the policy settings. In the computer policy we can see that Allow basic authentication is set to disabled.

Since this is a security risk (there is no encryption with basic authentication) and only a few device really need to connect, We decided to create a new policy that enabled this for only admins that will need to use Exchange Online PowerShell and use security filtering to only apply to a security group with the required devices.

Once basic authentication was re-enabled I was able to connect without issue.

If you have this issue and apply either the CIS or the Windows 10 baseline policy this could be the cause.

VMware PowerCLI Integrated Authentication Issues

Recently we have been having an issue with VMware PowerCLI not passing through the users credentials when running Connect-viserver to connect to our vCenter servers.

This has been causing problems when trying to use scheduled task to automated reports and run remediation task like removing old snapshots or reporting on VMware Tools versions as it is prompting for credentials.

For integrated authentication to work, the vCenter servers needs to be setup to allow single sign on for the domain that you will be connecting from, so confirm that your Active Directory Identity source is added and that SSO works from the web client. If not, complete this first before trying to use PowerCLI with integrated authentication.

I have SSO configured and tested so this wasn’t my issue.

When using the older version of PowerCli version 6.5 and below we had no issue with integrated authentication and would connecting to vCenter server without prompting me for credentials.

We could continue to use the old version of PowerCLI but we would be missing out on improvement and new commandlets so I wanted to try and get the newer version working for automated task.

First we install the VMware.PowerCLI module using Install-Module VMware.PowerCLI, the current version is 12.1

When I tried to connect using this version of PowerCLI I get prompted for a user name and password and this is what is stopping my automated task from running.

There are a few ways to workaround the prompt and this can also be used to confirm if SSO is working correctly. One way would be to add credential store item that can be used for connecting.

To add this use the VICredentialstore commandlet.

New-VICredentialStoreItem -User domain\username -Password Password -Host vc.domain.local -File C:\Temp\vicreds.xml

This outputs the credential to an xml file that can then be imported and called using Connect-VIserver.

$logon = Get-ViCredentialStoreItem -File C:\Temp\vicreds.xml
Connect-VIServer -Server $logon.Host -User $logon.User -Password $logon.Passwor

This works but I don’t really want to have a xml file that has information saved to it and someone might remove the folder or file by mistake.

The other way would be similar but using a txt file with the converted to a secure string password but again this relies on a file which is not ideal and not really all that secure.

The last option and one that I wouldn’t recommend at all is to hard code a username and password in the script.

So now that we have gone through some work arounds I decided to have a look at the actually problem .

The above proves that authenticating against AD is working so I knew it wasn’t an account or SSO issue so it had to be an issue with PowerCLI itself.

I connect using a my user name and passwords and it connect without issue.

I then check the VPX log under /storage/log/vmware/vpxd to see if there are an issue but I didn’t see any issues.

Next I tried to use the -verbose parameter to return more information on what exactly connect-viserver was doing,

This then returned an error for TLS.

I next checked the settings on PowerCLI configuration settings.

To check the PowerCLI configuration use

Get-PowerCLIConfiguration

When checking the configuration the Invalidcerificaeaction was set to unset.

I changed this setting to warn instead of unset

Once this setting was changed I can now connect to PowerCLI with integrated authentication, I do get a long warning message though I could set this to ignore and this returns no warning or error.

There are two ways around this instead of changing the above setting. One is to install the the certificate as a trusted root certificate so that the cert is trusted.

Or if you have an internal certificate authority you to replace the default VMware cert with an internal cert.

Once this is done the connection work without requiring manual intervention and my automated scripts can be run using scheduled tasks again with the latest version of PowerCLI. Hopefully this will be helpful to anyone else having this issue.

Windows Query Remote Group Membership Using PowerShell

We needed to do an audit on privileged group membership on workstations. There are many way to do this but using PowerShell to query WMI remotely was the method that we choose as we had to do a few thousand and I wanted to do the checks in groups. .

I decided to write a script that will take the list of devices to be checked from a txt file and then use parameter for the export path and groups to be search.

In this script we will use the class Win32_Group below is a link to the Microsoft Docs

Win32_Group class – Win32 apps | Microsoft Docs

When querying WMI we can use filters to limit the results. If you run query without filtering the command will return all groups even those in AD. See below command and the returned results.

Get-WmiObject  -Class Win32_GroupUser | Select-Object GroupComponent,PartComponent,PSComputerName

If we use a filter we can then reduce these by using domain which will be the local machine name and the local group name.

Get-WmiObject -Class Win32_GroupUser -Filter "GroupComponent=""Win32_Group.Domain='LAB-Host01',Name='Administrators'""" | Select-Object GroupComponent,PartComponent,PSComputerName

Now that we have a filter we can use variables to specific the host name from the text file with computer names and group names.

In the script these will be set using the complist and groups variables.

Below are two examples of how the script can be run against one group or multiple groups

.\Get-RemoteGroupMembers -CompList c:\Temp\Comps.txt c:\Temp\Results -groups “Administrators”

For multiple groups just add a , between the names and double or single quotes if there are spaces in the group name.

.\Get-RemoteGroupMembers -CompList .\Comps.txt -exportPath .\ -groups “Administrators”,”Remote Desktop Users”

The full script is on my github page. As always any scripts should be tested before run in production.

Scripts/Get-RemoteGroupMembers at master · TheSleepyAdmin/Scripts (github.com)

Export Remote Shares and Folder permissions using PowerShell

We have recently been looking to audit some Windows servers shares and folder permissions. I have previously used a script to export folder permissions, so some of this script will be from that previous script. The main difference is that we will be using WMI query to get the list of shares and a looping through specified servers.

To get the list of shares we will use the Win32_Share WMI class and filtered out the default shares.

Get-WmiObject -ComputerName $Server -Class win32_share -Filter "Description != 'Remote Admin' and Description != 'Default share' and Description != 'Remote IPC' and Description != 'Printer Drivers'" | Select-Object Name -ExpandProperty Name

The full script that will be used is located on my Github repository, see link below.

Scripts/Get-SharesAndPermissions.ps1 at master · TheSleepyAdmin/Scripts (github.com)

To run the script use the below and update the exportpath and servers. To add multiple server just a comma between server names.

.\Get-SharesAndPermissions.ps1 -ExportPath D:\Scripts\Folder_Permissions\Export -Servers Server1, Server2

Once the script has completed the result will be export to a csv in the exported folder path.

Export GPO assignments using PowerShell

Recently we wanted to do a review of all our Active Directory Group policy objects (GPO’s), we wanted to see what GPO’s where not assigned or what OU they where assigned, so that we could try to consolidate or remove unused GPO’s.

There was a couple of hundred in each domain, so I didn’t want to have to check each one manually.

There is a PowerShell command that you can run to list all GPO’s but it doesn’t show assignments

GPO1

To get more information on the GPO we can run the command Get-GPOReport which let’s you create either a HTML or XML report.

In this case I want to use an XML as I want to pull information from the xml report, the only issue is getting data directly from a XML report is a bit difficult.

GPO2To read an XML report in PowerShell you can use the typecast to XML by using [xml] in front of the variable which should make querying the content easier. GPO3The only part of the XML that I really want currently is the LinksTo, as this shows where the GPO is assigned. GPO4Once I had all this information I was then able to create the full script. I will put the script up on GitHub since it easier for people to copy the script file.

https://github.com/TheSleepyAdmin/Scripts/tree/master/ActiveDirectory/GPO

Below is the script running GPO5This is what the export will look like.GPO6

 

 

 

 

 

Export folder permission using PowerShell

Recently we where moving folder and shares from one server to another. We need to confirm that the folder and permissions were the same on both the old and new share.

To do this I used PowerShell to export the pre and post move permissions and compare the results.

The two commands to get most of the information will be Get-ChildItem and Get-Acl.  The only part of the scripts that will need to be changed is the the export file name to give it a custom name.

Below is the link to the script I will be using.

https://github.com/TheSleepyAdmin/Scripts/tree/master/General/Folder%20Permission

To run the script there will be two mandatory parameters and the command should look like the below. (If you want to look up all subfolders also then just change line 14 and add -Recurse to Get-ChildItem command)

.\Get-FolderPermissions.ps1 -FolderPath \\lab-host01\sources -ExportPath D:\Scripts\Folder_Permissions\Export

I ran the script and changed the exported csv name to pre and post to be used to compare.
Per1
Below is what the export should look like.
Per2
Per3
Once the pre and post export are done we can use compare-object to find any differences.
Just need to update the import-csv paths, I was moving to a share that would have the same FQDN so if that is not the case you can removed the FolderPath from the compare-Object property otherwise all result will not match.
Below is the link to the script I will be using.
Per4Per5
Below is the export results showing the difference between the pre and post move Per6

Check Scheduled task for run as account PowerShell

Recently I needed to check for all scheduled task running on servers using a certain account as the password needed to be updated.  To search for these scheduled task I used schtask.exe

https://docs.microsoft.com/en-us/windows/win32/taskschd/schtasks

I wanted to integrate this with PowerShell so that I could do some connection tests, loop through a list of devices and output the results to CSV.  Below is the script I created it has three mandatory parameters for the computer list, run as account and export path.

The link to the script is below.

https://github.com/TheSleepyAdmin/Scripts/blob/master/General/Scheduled%20task/Check_RunAsAccount.ps1

To run the script supple the three parameter like below. I used Check_RunAsAccount as the scrip name.

.\Check_RunAsAccount.ps1 -CompList D:\Scripts\Task_Scheduler\Complist.txt -RunAsAccount test1 -ExportLocation D:\Scripts\Task_Scheduler\

SCk1SCk4Once the script has completed there will be a result csv and if there are any non responding devices there will be a second csv. SCk5The export result will look like the below. SCk2

 

Remove WINS & Disable Netbios over Tcpip PowerShell

As part of  a recent decommission / security audit, we needed to remove an old WINS server. For desktop client this is fairly easy as they are all assigned through DHCP so it was just a case of removing WINS from the DHCP scope options.

For the hundreds of servers it is set manually, which to remove one by one would take a long time and be pretty boring for the person tasked with it. So I decided the simplest option would be to use PowerShell.

First I wanted to check if servers had WINS enabled so I could reduce the amount of server I would need to run the disable script against.

I am going to use Get-WmiObject and the Win32_NetworkAdapterConfiguration class as this is the simplest way I found to do this in PowerShell.

We will use a text file with a list of server names and a variable called $WINSServer that will be used to filter only network interfaces that have WINS set.

Below is the script to check for WINS and output to PowerShell windows I am just getting all adapters that have WINSPriamryServer value set to the IP in the $WINSServer variable and then selecting the objects to be outputted.

If you wanted to export to a csv or text file just add a | after the WINSPrimaryServer at the end of the script and do either Out-file or Export-csv and the path to export too. 

Below is the link to the script location on Github it called Check-Wins.ps1

https://github.com/TheSleepyAdmin/Scripts/tree/master/General/Remove%20Wins

WINSTo remove the WINS IP and set NetBios option, we will use the set method in the WMI class.

Below is the link to the script location on Github it called Remove-Wins.ps1 :

https://github.com/TheSleepyAdmin/Scripts/tree/master/General/Remove%20Wins

WINS01Once the script has run WINS should be removed and NetBios over Tcpip should be disabled this can be checked under the advanced properties on the NIC.

WINS02