16 September 2022

Disable Exchange Online Remote PowerShell for users with PowerShell

 A regular user has no need for remote PowerShell access to Exchange Online.
So, we're going to disable it.

This is not as easy as you might think, I saw an article by "The Cloudtechnologist" (disable-exchange-online-remote-powershell-for-users-as-a-scheduled-task) but this is for Global admins only. What if you have roles assigned to Exchange Admins that are not Global admins?

This might help:

At line26; edit your username
At line 28 to 40 you disable all user accounts that are synced from on-premises
At line 44 to 56 you disable all guest users
At line 59 to 71 you disable all roommailboxes
At line 73 to 80 you can create a list with all account that are still enabled for remote PowerShell, go through the list manually and use that list to disable the access for the remaining users.

You might need to change the filters to something that works for you, and as always with stuff found on the interwebs, test test test.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
#region functions
<#
.SYNOPSIS
Script to disable Exchange Online RemotePowerShell access for users, guests and RoomMailboxes
.DESCRIPTION
Disables Exchange Online RemotePowerShell acces for users, guests and roommailboxes, and let's the remaining users be disabled by file list.
.PARAMETERS
None
.EXAMPLE
Disable-EXOPowerShellForUsers
.By
Edwin van Brenk
.For
Company
.Version
1.0
.Date
13-09-2022
.SOA
SOA-212
#>


Import-Module -Name ExchangeOnlineManagement

Connect-ExchangeOnline -UserPrincipalName username@company.com

# Dir synced users

Write-Host "Disable-EXOPowerShellForUsers: process: Getting all dirsynced users from tenant"
$Users = Get-User -ResultSize unlimited | where-object {$_.isdirsynced -eq 'True'}

foreach ($User in $Users) {
            try {
                Write-Host "Disable-EXOPowerShellForUsers: process: Updating $($User.WindowsEmailAddress)"
                Set-User -Identity $User.WindowsLiveID -RemotePowerShellEnabled $false -Confirm:$false
            }
            catch {
                Write-Warning "Something went wrong with $($User.WindowsEmailAddress)"
                continue
            }
            $user = $null
        }

$number = $Users.Count
Write-host "$number of users have been updated"

# Guest users

Write-Host "Disable-EXOPowerShellForUsers: process: Getting all guest users from tenant"
$GuestUsers = Get-User -ResultSize unlimited | where-object {$_.RecipienttypeDetails -eq 'GuestMailUser'}

foreach ($GuestUser in $GuestUsers) {
            try {
                Write-Host "Disable-EXOPowerShellForUsers: process: Updating $($GuestUser.Identity)"
                Set-User -Identity $GuestUser.WindowsLiveID -RemotePowerShellEnabled $false -Confirm:$false
            }
            catch {
                Write-Warning "Something went wrong with $($GuestUser.WindowsLiveID)"
                continue
            }
            $GuestUser = $null
        }

$number = $GuestUsers.Count
Write-host "$number Guest users have been updated"

# Teams Rooms

Write-Host "Disable-EXOPowerShellForUsers: process: Getting all TeamRooms from tenant"
$TeamsRooms = Get-User -ResultSize unlimited | where-object {$_.RecipienttypeDetails -eq 'RoomMailbox'}

foreach ($TeamsRoom in $TeamsRooms) {
            try {
                Write-Host "Disable-EXOPowerShellForUsers: process: Updating $($TeamsRoom.WindowsEmailAddress)"
                Set-User -Identity $TeamsRoom.WindowsLiveID -RemotePowerShellEnabled $false -Confirm:$false
            }
            catch {
                Write-Warning "Something went wrong with $($TeamsRoom.WindowsLiveID)"
                continue
            }
            $TeamsRoom = $null
        }

$number = $TeamsRooms.Count
Write-host "$number TeamsRooms have been updated"

# Block for a list of users

$UserList = Get-Content "C:\Users\Username\OneDrive - Company\Security Optimization Assesment\2022\Scripts\userlist.txt"
$UserList | foreach {Set-User -Identity $_ -RemotePowerShellEnabled $false}


Get-User -ResultSize unlimited -Filter 'RemotePowerShellEnabled -eq $true' | Select-Object Name, WindowsLiveID, WindowsEmailAddress, RecipientType, RecipientTypeDetails |` 
export-csv -Path "C:\Users\Username\OneDrive - Company\Security Optimization Assesment\2022\Scripts\AcceptedEnabledRemotePowerShellUserList.csv"

<#
-To display only those users who don't have access to Exchange Online PowerShell, run the following command:

Get-User -ResultSize unlimited -Filter 'RemotePowerShellEnabled -eq $false'

-To display only those users who have access to Exchange Online PowerShell, run the following command:

Get-User -ResultSize unlimited -Filter 'RemotePowerShellEnabled -eq $true'
#>

24 August 2022

Corporate Windows 10 devices pointing to WSUS

If you're not on Twitter, get on Twitter.

Famous words from @samilaiho, and you know why?
Because of tweets like these:

You should check your settings as this is a common error and can prevent a lot of nasty problems.

When Windows is pointed at a WSUS server, it stops trying to repair corrupted components from Microsoft servers.

Check the whole thread here:
https://twitter.com/SwiftOnSecurity/status/1561827619242475521




14 July 2022

Update-Module - Module PowershellGet was not installed by using Install-Module, so it cannot be updated.

Well there we go again, something that should be easy and take 5 minutes turns in to a 3 hour job.

How we got here?

Install-Module : A parameter cannot be found that matches parameter name ‘AllowPrerelease’

Okay, so if that doesn't work, then try to update it.
No bueno, this is because PowerShellGet comes builtin with Windows 2016 but it's on older version. If you want all bells and whistles you need to install newest version from PowerShellGallery.

That doesn't really help.
But what does help is going full mental.
PowerShellGet 1.0.0.1 does not update properly.

Now try the aggressive way of  forcing your will onto this module.

Browse to C:\Program Files\WindowsPowerShell\Modules\
Open the folder C:\Program Files\WindowsPowerShell\Modules\PowershellGet and delete the subfolder 1.0.0.1
Open the folder C:\Program Files\WindowsPowerShell\Modules\PackageManagement and delete the subfolder 1.0.0.1
Browse to C:\Program Files (x86)\WindowsPowerShell\Modules\
Open the folder C:\Program Files (x86)\WindowsPowerShell\Modules\PowershellGet and delete the subfolder 1.0.0.1
Open the folder C:\Program Files (x86)\WindowsPowerShell\Modules\PackageManagement and delete the subfolder 1.0.0.1

Now run Install-Module PowerShellGet -Force and Update-Module PowerShellGet

Check for correct installation and mudule version with Get-Command Install-Module.

Stopped deletion threshold exceeded - Azure AD Connect

After deleting a lot of old AD groups the Azure AD Connect sync stopped working.
We could start the sync no problem, but nothing was being synced.

In the Synchronization Service Manager we could see the following error:
stopped-deletion-threshold-exceeded

Turns out there is a limit of 500 items that get synced at once. Go over this and every thing stops. I had no idea this was in place, but its on by default. So in case of an accidental deletion your stuff gets saved , yay!

I came across this blogpost that explains just this problem, so Ali Tajran over at https://www.alitajran.com saved a couple of hours of trouble shooting and I learned something new đŸ˜„

First run PowerShell as administrator. Run the Get-ADSyncExportDeletionThreshold cmdlet to check both the objects DeletionPrevention and TresholdCount.
- DeletionPrevention is 1 (enable)
- ThresholdCount is 500 (default AD objects)



The solution for stopped-deletion-threshold-exceeded is to disable the export deletion threshold with PowerShell.

Step 1: Disable Azure AD Connect sync export deletion threshold with the Disable-ADSyncExportDeletionThreshold cmdlet.
PS C:\> Disable-ADSyncExportDeletionThreshold

DeletionPrevention ThresholdPercentage ThresholdCount
------------------ ------------------- --------------
                 0                   0            500
Step 2: Force sync Azure AD Connect

Force sync Azure AD Connect with PowerShell. The initial sync will do a full sync from AD on-premises to Azure AD. You can also run a Policytype Delta sync to only sync the changes, this is faster.
PS C:\> Start-ADSyncSyncCycle -PolicyType Initial
Step 3: Verify Synchronization Service status

In Synchronization Service Manager, check that the export status shows as success. In our example, the export did delete 5069 AD objects.
Stopped deletion threshold exceeded - Azure AD Connect after
Step 4: Enable Azure AD Connect sync export deletion threshold

Revert the change to protect AD objects from accidental removal. The default is 500 AD objects.
PS C:\> Enable-ADSyncExportDeletionThreshold -DeletionThreshold 500

DeletionPrevention ThresholdPercentage ThresholdCount
------------------ ------------------- --------------
                 1                   0            500

Source

28 June 2022

Windows Update error 0x8024401B

 Ah the dreaded error that WSUS throws at you.

There are several "solutions" to be found with a quick Google.

But in my case it was quite clear, on 5 identical machines 1 couldn't access the WSUS server on the network. So what's the difference?

Came across this post on docs.microsoft.com
https://docs.microsoft.com/en-us/troubleshoot/mem/configmgr/troubleshoot-software-update-scan-failures

The portion that stood out was the proxy bit. Checking the current settings on the machine that was working and the machine that wasn't working.

The working machine:

C:\windows\system32>netsh winhttp show proxy

Current WinHTTP proxy settings:

  Direct access (no proxy server).

And the non working machine:

C:\windows\system32>netsh winhttp show proxy

Current WinHTTP proxy settings:

Proxy Server(s) :  http=proxy.domain.lan:8080

    Bypass List     :  *.domain.lan;192.*

And there it was, well that's a quick fix:

netsh winhttp reset proxy


13 May 2022

No certificate templates could be found. You do not have permission to request a certificate from this CA, or an error occurred while accessing the Active Directory.

When trying to access the certsrv page to issue a certificate from a .req or .csr file you are presented the following error:

Now what...
One possibility could be that the Authentication settings in IIS on both CertEnroll & CertSrv has been changed to Anonymous Authentication Enabled & Windows Authentication Disabled.

Change it back to the default which are Anonymous Authentication Disabled & Windows Authentication Enabled and try again.

If the page still doesn't show the templates, you need to check the template version.

When duplicating a template, you are asked what version to make it and given an option of "Windows Server 2003" or "Windows Server 2008".


The certsrv web site is only compatible with the Windows Server 2003 based templates which I think corresponds to version 2. Ironically, this same limitation is present all the way through Windows Server 2012 R2. The certsrv site still can't use version 3 templates. Here's the related KB article:








25 March 2022

Azure AD Connect password hash sync stopped - no-start-credentials - failed-authentication

 The following error in the Office365 portal:


The following errors in the Syncronization service Manager:


And this event id in the event viewer:


Now the thing that threw me off was the "The supplied credential is invalid" error.
After confirming that the password set correctly ( https://docs.microsoft.com/en-us/azure/active-directory/hybrid/how-to-connect-sync-change-addsacct-pass#how-to-update-the-synchronization-service-with-new-password-for-ad-ds-account )

I remembered a case from a few weeks ago that had to do with cipher suites.

There was a difference in ciphers on the DC's and on the ADCS server (certificate authority).
The problem was that RC4 had been turned off, but the CA was requesting it.

After confirming my suspicion and checking the registry key on the AADConnect server:
HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\Parameters\SupportedEncryptionTypes with a value of 0x7ffffff8. I knew I had found the problem. After setting the value to "0x7ffffffc" and thereby turning on the RC4 cipher, restarting the "Microsoft Azure AD Sync" service and starting an export all password hashes started syncing again.

I have absolutely no idea why the sync would stop after not having the old and deprecated RC4 cipher turned on. But that is a question for another day.




28 February 2022

Azure ADConnect Access is Denied error code 5 - Azure ADConnect not syncing password hashes anymore

 After receiving a warning from Azure that the password sync has not run for 1 hour I started checking where this could come from.

The first place is to check the Office365 portal "Directory sync status" page under "Health":

https://admin.microsoft.com/Adminportal/Home?#/dirsyncmanagement

The second place is to check the "Azure AD Connect" page in the Azure portal:

https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/AzureADConnect

Now you know that there is a problem, the next step is to check the Azure ADConnect server itself.
After opening the Synchronization Service Manager you are greated with this error, well in my case anyway:

The thing to check is the account that is being used to sync with:

Import-Module
"C:\Program Files\Microsoft Azure Active Directory Connect\AdSyncConfig\AdSyncConfig.psm1" Get-ADSyncADConnectorAccount Copy the "ADConnectorAccountName"

Copy the "ADConnectorAccountName"

Run the following commandlet's with your ADConnectorAccountName value:
Set-ADSyncMsDsConsistencyGuidPermissions -ADConnectorAccountName "MSOL_1234abcd1234" -ADConnectorAccountDomain domain.ads

Set-ADSyncPasswordHashSyncPermissions -ADConnectorAccountName "MSOL_1234abcd1234" -ADConnectorAccountDomain domain.ads

Set-ADSyncPasswordWritebackPermissions -ADConnectorAccountName "MSOL_1234abcd1234" -ADConnectorAccountDomain domain.ads

Set-ADSyncUnifiedGroupWritebackPermissions -ADConnectorAccountName "MSOL_1234abcd1234" -ADConnectorAccountDomain domain.ads

And now run a full sync

Start-ADSyncSyncCycle -PolicyType Initial -Verbose

17 February 2022

Reset the Teamsmeetingpolicy to "Global" for all users

Some users reported that they couldn't use certain features, such as "transcription".

When comparing myself with the affected user I noticed that my Teamsmeetingpolicy had no policy set.
I would expected it to show "Global". Turns out the "Global policy" isn't a user policy and therefore doesn't show up with a name.

The affected user did have a policy set: "RestricetedAnonymousAccess".

Now I had to give those users the "Global" Teamsmeetingpolicy and came up with this:

 1
2
3
4
5
6
7
$users = Get-CsOnlineUser -ResultSize unlimited
foreach($User in $Users)
{
$userId = $User.UserPrincipalName
Write-Host $userId
Grant-CsTeamsMeetingPolicy -Identity $userId -PolicyName $Null -ErrorAction SilentlyContinue
}

This resets all users to no policy and thus the "Global" policy.

Maybe it is a bit much for large environments, it takes quite a long time to run.
With some filtering this could be done quicker.

 1
2
3
4
5
6
7
$users = Get-CsOnlineUser -ResultSize unlimited -Filter {Teamsmeetingpolicy -ne $null}
foreach($User in $Users)
{
$userId = $User.UserPrincipalName
Write-Host $userId
Grant-CsTeamsMeetingPolicy -Identity $userId -PolicyName $Null -ErrorAction SilentlyContinue
}

21 January 2022

Count your Active directory objects

When installing AADConnect you need to size your server according to this table:

Hardware requirements for Azure AD Connect

The following table shows the minimum requirements for the Azure AD Connect sync computer.


To quickly count all objects in your Active Directory do this:

1|
(Get-ADObject -SearchBase "dc=Mydomain,dc=com" -LDAPFilter "(objectCategory=*)").Count

This will show all objects.

If you want to know how many user, group, computer account, distribution group and security group objects there are do this:
1|
2|
3|
4|
5|
6|
7|
$ADUser = (Get-AdUser -Filter *).Count
$ADGroup = (Get-ADGroup -Filter *).Count
$ADComputer = (Get-ADComputer -Filter *).Count
$Distribution_Groups = (Get-ADGroup -Filter {GroupCategory -eq "Distribution"}).count
$Security_Groups = (Get-ADGroup -Filter {GroupCategory -eq "Security"}).count
$ADObjects = $ADUser + $ADGroup + $ADComputer + $Distribution_Groups + $Security_Groups
$ADObjects

13 January 2022

Enable TLS 1.3 on Windows Server 2022

HTTP/3 support is an opt-in option on Windows Server 2022 via a registry key named "EnableHttp3" with value 1 at "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\HTTP\Parameters".
Running this command from an elevated prompt will create the key:

reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\HTTP\Parameters" /v EnableHttp3 /t REG_DWORD /d 1 /f

Then restart the http.sys service or reboot Windows to apply the setting.

It is likely the web service will need to advertise it is available over HTTP/3 as well using “Alt-Svc” headers in HTTP/2 responses (though this can also be done using HTTP/2 ALTSVC frames). This allows clients who connect over HTTP/2 to learn the service’s HTTP/3 endpoint and use that going forward. This is done by sending an HTTP/3 ALPN (“Application-layer Protocol Negotiation”) identifier with HTTP/2 responses advertising a specific version of HTTP/3 to use for future requests. Sending the ALTSVC frame can be done by http.sys. That can be enabled by setting the “EnableAltSvc” registry key with the command below.

reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\HTTP\Parameters" /v EnableAltSvc /t REG_DWORD /d 1 /f

After that run the following in PowerShell:

Enable-TlsCipherSuite -Name TLS_CHACHA20_POLY1305_SHA256 -Position 0

Finally, add the HTTP/3 response header to your IIS website. 

Under the HTTP Response Headers, add a custom HTTP response header with the following information:
Name: alt-svc
Value: h3=":443"; ma=86400; persist=1


After adding the response header, enabling the cipher suites and merging the registry keys, reboot your Windows Server 2022 server.

06 January 2022

Set Outlook delegate permission with PowerShell

Sometimes my users ask things I didn't know existed.

The question I got was: Why can't I see the meeting invitations sent to the persons mailbox and calendar I manage in my own inbox? I knew it had to do with delegates, but thought that could only be set by the owner of the mailbox. Turns out I was wrong, it can be set by the admin with PowerShell.

It's always PowerShell đŸ˜œ

First check the current permissions:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Get-MailboxFolderPermission "TargetUser:\agenda"

FolderName           User                 AccessRights                           SharingPermissionFlags
----------           ----                 ------------                           ----------------------
Agenda               Default              {AvailabilityOnly}
Agenda               DestinationUser      {PublishingEditor}                     Delegate
Agenda               SomeUser             {Reviewer}
Agenda               SomeUser2            {LimitedDetails}
Agenda               TargetUser           {LimitedDetails}
Agenda               SomeUser3            {Editor}                               Delegate

Then remove all the existing permissions for the destination user:

 1
2
3
4
5
6
Remove-MailboxFolderPermission "TargetUser:\agenda" -User DestinationUser

Confirm
Are you sure you want to perform this action?
Removing mailbox folder permission on Identity:"TargetUser:\agenda" for user "DestinationUser".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [?] Help (default is "Y"):

There's a few things to note before setting the permissions. To be able to grant the delegate permissions and all options the AccessRight has to be "Editor", the SharingPermissionsFlags need to be separated with a comma, and the option to SendNotificationToUser expects a boolean value so set a $False for no notification or $True to send a notification to the user:

 1
2
3
4
5
add-MailboxFolderPermission "TargetUser:\agenda" -User DestinationUser -SharingPermissionFlags delegate,canviewprivateitems -AccessRights editor -SendNotificationToUser $true

FolderName           User                 AccessRights                           SharingPermissionFlags
----------           ----                 ------------                           ----------------------
Agenda               DestinationUser      {Editor}                               Delegate, CanViewPrivateItems

That's it