30 May 2023

Delete mailbox for user without license - is not within a valid server write scope

After trying to remove a mailbox in Exchange online for a user that had a mailbox assigned on-prem, but then the license got revoked and the sync didn't go as it should you are left with a mailbox in Exchange Online for a user that doesn't have a license for it.

So this has to go, but when trying to delete the mailbox like so, you get the following error:

Remove-Mailbox usermailbox@domain.com
Remove-Mailbox: ExE71684|Microsoft.Exchange.Configuration.ObjectModel.ProvisioningValidationException|
The following error occurred during validation in agent 'Windows LiveId Agent': 'Unable to perform the save operation.
'usermailbox' is not within a valid server write scope.'

Since the user must be retained and only the mailbox has to be deleted we can delete just the mail user:

Connect-MsolService
Get-MsolUser -UserPrincipalName usermailbox@domain.com | Remove-MsolUser -Force

Now see what we did there:

Get-MsolUser -UserPrincipalName usermailbox@domain.com | fl
Get-MsolUser : User Not Found.  User: usermailbox@domain.com.
At line:1 char:1
+ Get-MsolUser -UserPrincipalName usermailbox@domain.com | fl
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (:) [Get-MsolUser], MicrosoftOnlineException
    + FullyQualifiedErrorId : Microsoft.Online.Administration.Automation.UserNotFoundException,Microsoft.Online.Administration.Automation.GetUser

And it's gone.

26 May 2023

Sign certificates in Bulk - Create .PEM, .CER and .RSP certificate files from .CSR in bulk with PowerShell

Every time the intermediate certificate expires this is a recurring job.

At least 200 certificate requests need to be signed. Now I could be doing this by hand, but that would take forever would be super annoying and tedious.

So once again PowerShell to the rescue.
Put the .csr files in a directory and adjust the path in the script to match it.
Then create the output folder and adjust the patch in the script to match it.
Choose the template name for the certificate you want to request, mine was a "webserver" request.

You will be asked to click OK to select the CA for each certificate. (I know, but still beats creating them all by hand)

 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
<#
Title         : Get-CertificateFromCSR.ps1
Version       : 1.0
Created by    : Edwin van Brenk © 2023
For           : vanbrenk.blogspot.com
Date          : 26-05-2023

.Synopsis
   Creates a certificate from a .csr file
.DESCRIPTION
   This script requests a .pem file from a .csr to the specified Certificate Authority
   Save the .csr's in the $outpath folder.
.EXAMPLE
   Just run it in a ISE window, you are prompted to select the CA.
#>



# Invoke the CertReq.exe command to sign a certificate request

# certreq -submit <Path to request file> <Path to output cert file>
# certreq -submit certRequest.req certnew.cer certnew.pfx

$CSRs = Get-ChildItem "C:\Scripts\Get-BulkCertFromCSR\CSR\2023"
$OutPath = New-Item "C:\Scripts\Get-BulkCertFromCSR\CSR\IssuedCertificates\2023" -ItemType Directory -Force

ForEach($CSR in $CSRs){
    $FileOutCER = Join-Path $OutPath "$($CSR.BaseName).cer"
    $FileOutPEM = Join-Path $OutPath "$($CSR.BaseName).pem"
    CertReq -submit -attrib "CertificateTemplate:Webserver" $CSR.FullName $FileOutCER $FileOutPEM
}

25 May 2023

New user cannot login after creation - User must change password at next logon - Azure ADConnect

And yes, here's another thing I learned today.

When a new user is created in on-premises AD and it has the "User must change password at next logon" flag set it does not get synced automatically to AzureAD by default. đŸ˜²


Note the word "automatically".

Turns out you have to configure Azure ADConnect to sync it.

If you look at the default setting it this:



To Enable it, you can use the below command.
Set-ADSyncAADCompanyFeature -ForcePasswordChangeOnLogOn $true


After this you need to enforce the cloud password policy.

To do this first connect to the MSOL service
Connect-MsolService

And then run the following:
Set-MsolDirSyncFeature -Feature EnforceCloudPasswordPolicyForPasswordSyncedUsers

When requested to enable the feature, type Yes and confirm the execution.



NOTE if synchronized users need to have the password to never expirer (let say for service accounts for example), you will need to manually reassign the DisablePasswordExpiration value to the PasswordPolicies after enabling this feature

Set-AzureADUser -ObjectID <User Object ID> -PasswordPolicies "DisablePasswordExpiration"

The flaw in the configuration has been resolved in Azure AD Connect 2.0.3.

In the previous version, when an expired password was "unexpired" by clearing the "Must change password at next logon" flag, the unexpired password was not synchronized with Azure Active Directory (Azure AD) unless the password itself was changed. This meant that users had to continue using their old expired password when signing in to Azure AD.

However, in Azure AD Connect 2.0.3 and later versions, passwords are reevaluated when an expired password is "unexpired," regardless of whether the password itself is changed. If the "Must change password at next logon" flag is cleared, indicating that the password is no longer set to change, the unexpired status and the password hash are now synced to Azure AD. This improvement allows users to use their unexpired password when signing in to Azure AD.

To benefit from this fix, it is recommended to upgrade to the latest version of Azure AD Connect if you are using an older version. It's worth noting that Azure AD Connect 2.0.3 requires Windows Server 2016 or a newer version. You can find guidance on how to upgrade Azure AD Connect to the latest version if you are currently using an older version.

Simply list all Shared Mailboxes from On-premises and in Exchange Online with PowerShell

Hey there, fellow tech enthusiasts!
Today, I have some lines to drop about how to simply list all your shared mailboxes.
In this case I wanted to get all email addresses.

Step 1: Connect to your environment

Connect to you on-premises Exchange environment.

Step 2: Retrieve On-Premises Shared Mailboxes

Let's start by fetching all the remaining shared mailboxes from your on-premises environment. Open up your PowerShell console and execute the following:

$OnPremSharedMailboxes = Get-Mailbox -RecipientTypeDetails SharedMailbox -ResultSize unlimited | Select-Object PrimarySmtpAddress

Step 3: Retrieve the remote shared mailboxes

Run the following commands

Get all mailboxes
  $RemoteMailboxes = Get-RemoteMailbox -ResultSize unlimited

Get all remotesharedmailboxes and filter only the smtpaddress
  $RemoteMailboxes | Where-Object { $_.RecipientTypeDetails -eq "RemoteSharedMailbox" } | select PrimarySmtpAddress

Step 5: Consolidate the Results

It's time to bring everything together and create a unified list of shared mailboxes. Run the following commands to consolidate the on-premises and Exchange Online results:

  $AllSharedMailboxes = $OnPremSharedMailboxes + $ExchangeOnlineSharedMailboxes

With this command, you'll combine the arrays of on-premises and Exchange Online shared mailbox addresses into a single array called '$AllSharedMailboxes'. You can make other choices in your filter to generate a different list.

Step 6: Display the Results

  $AllSharedMailboxes

And just like that, your PowerShell console will present you with a complete list of shared mailbox addresses, combining the best of both on-premises en Exchange Online.