{"id":3273,"date":"2022-08-12T13:15:59","date_gmt":"2022-08-12T18:15:59","guid":{"rendered":"https:\/\/microsoftgeek.com\/?p=3273"},"modified":"2022-08-12T13:33:10","modified_gmt":"2022-08-12T18:33:10","slug":"how-to-connect-to-exchange-online-powershell-via-v2-module","status":"publish","type":"post","link":"https:\/\/microsoftgeek.com\/?p=3273","title":{"rendered":"How to Connect to Exchange Online PowerShell via v2 Module"},"content":{"rendered":"\n<p>In this article, you will learn how to prepare to use the EXO V2 module to run Exchange Online unattended scripts with app-only modern authentication. You\u2019ll learn how to:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Register a new app in Azure Active Directory and enable its\u00a0service principal.<\/li><li>Assign API permissions and roles.<\/li><li>Generate and upload a self-signed certificate.<\/li><li>Use the app and certificate to authenticate and connect to Exchange Online PowerShell.<\/li><\/ul>\n\n\n\n<p>With that said, don\u2019t expect to see many click-and-point instructions in this article. Most of the walkthrough instruction will be done in PowerShell.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-setting-up-app-only-authentication-using-powershell\">Setting Up App-Only Authentication using PowerShell<\/h2>\n\n\n\n<p>You might be used to using a generic account, which is often referred to as a service account to run your PowerShell scripts. That type of account is a \u201c<em>shared<\/em>\u201d account in nature. Anyone who knows that account\u2019s credential can use it to log in and perform all sorts of admin stuff on your organization; that is a security concern.<\/p>\n\n\n\n<p>The app-only authentication&nbsp;<em>attempts<\/em>&nbsp;to solve that security concern. App-only authentication requires the use of an Azure AD app with service principal and selected permissions and role. Use token or certificate to authenticate.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-creating-an-azure-ad-application-with-api-permissions\">Creating an Azure AD Application with API Permissions<\/h3>\n\n\n\n<p>The first step is to create a new app in Azure AD with the right API permissions. First, open an elevated Windows PowerShell (run as admin) and make sure to&nbsp;<em><a href=\"https:\/\/docs.microsoft.com\/en-us\/powershell\/module\/azuread\/connect-azuread?view=azureadps-2.0\" target=\"_blank\" rel=\"noreferrer noopener\">connect to Azure AD<\/a><\/em>.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/adamtheautomator.com\/wp-content\/uploads\/2021\/05\/connect-azuread-min.gif\" alt=\"Connect to Azure AD\" class=\"wp-image-8616\"\/><figcaption>Connect to Azure AD<\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>The code below will register a new app in Azure AD with the name&nbsp;<em>Exo_V2_App<\/em>&nbsp;and assign the&nbsp;<em>Exchange.ManageAsApp<\/em>&nbsp;permission of the&nbsp;<em>Office 365 Exchange Online<\/em>&nbsp;API.<\/p>\n\n\n\n<p>If you prefer to use a different name for your app, edit the value of the&nbsp;<code>$appName<\/code>&nbsp;variable in the code below. Copy the code and run it in PowerShell.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># CODE TO REGISTER APP, ASSIGN API PERMISSIONS, AND ENABLE SERVICE PRINCIPAL\n## Define the client app name\n$appName = 'Exo_V2_App'\n\n## Get the Office 365 Exchange Online API details.\n$api = (Get-AzureADServicePrincipal -Filter \"AppID eq '00000002-0000-0ff1-ce00-000000000000'\")\n\n## Get the API permission ID\n$permission = $api.AppRoles | Where-Object { $_.Value -eq 'Exchange.ManageAsApp' }\n\n## Build the API permission object (TYPE: Role = Application, Scope = User)\n$apiPermission = &#91;Microsoft.Open.AzureAD.Model.RequiredResourceAccess]@{\n    ResourceAppId  = $api.AppId ;\n    ResourceAccess = &#91;Microsoft.Open.AzureAD.Model.ResourceAccess]@{\n        Id   = $permission.Id ;\n        Type = \"Role\"\n    }\n}\n\n## Register the new Azure AD App with API Permissions\n$myApp = New-AzureADApplication -DisplayName $appName -ReplyUrls 'http:\/\/localhost' -RequiredResourceAccess $apiPermission\n\n## Enable the Service Principal\n$mySP = New-AzureADServicePrincipal -AppID $myApp.AppID\n\n## Display the new app properties\n$myApp | Format-List DisplayName,ObjectID,AppID\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>The demonstration below shows the code in action. In the end, it will present the\u00a0DisplayName,\u00a0ObjectID, and\u00a0AppID\u00a0properties of the new app is displayed.\u00a0$myApp\u00a0variable stores these properties. The\u00a0$mySP\u00a0variable holds the property values of the service principal.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/adamtheautomator.com\/wp-content\/uploads\/2021\/05\/Register-App-min.gif\" alt=\"Register a new Azure AD app\" class=\"wp-image-8617\"\/><figcaption>Register a new Azure AD app<\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Export the property values of the application using this command below.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$myApp | Export-Csv -NoTypeInformation \"$($appName).csv\"\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-assigning-an-azure-ad-role-to-the-application\">Assigning an Azure AD Role to the Application<\/h3>\n\n\n\n<p>After creating the app, the next step is to assign an&nbsp;<em><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/users-groups-roles\/directory-assign-admin-roles\" target=\"_blank\" rel=\"noreferrer noopener\">Azure AD role<\/a><\/em>&nbsp;to the app\u2019s service principal. You\u2019ll need to decide the type of role you should assign to your app.<\/p>\n\n\n\n<p>The valid supported roles for Exchange Online V2 are these below.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Company\u00a0administrator<\/li><li>Compliance\u00a0administrator<\/li><li>Security\u00a0reader<\/li><li>Security\u00a0administrator<\/li><li>Helpdesk\u00a0administrator<\/li><li>Exchange\u00a0Administrator<\/li><li>Global\u00a0Reader<\/li><\/ul>\n\n\n\n<p>You should only assign the least privileged role that you deem appropriate to your script. In this example, the code below will assign the&nbsp;<em>Exchange&nbsp;Service&nbsp;administrator<\/em>&nbsp;role to the app\u2019s service principal.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>## The role to assign to your app\n$directoryRole = 'Exchange Service Administrator'\n\n## Find the ObjectID of 'Exchange Service Administrator'\n$RoleId = (Get-AzureADDirectoryRole | Where-Object {$_.displayname -eq $directoryRole}).ObjectID\n\n## Add the service principal to the directory role\nAdd-AzureADDirectoryRoleMember -ObjectId $RoleId -RefObjectId $mySP.ObjectID -Verbose\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>When you run the command above in PowerShell, you should see an output similar to the one shown in the demo below.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/adamtheautomator.com\/wp-content\/uploads\/2021\/05\/Add-Role-min.gif\" alt=\"Assigning an Azure AD role to the app\" class=\"wp-image-8618\"\/><figcaption>Assigning an Azure AD role to the app<\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-generating-and-attach-a-self-signed-certificate-to-the-application\">Generating and Attach a Self-Signed Certificate to the Application<\/h3>\n\n\n\n<p>The next step is to generate a self-signed certificate and attach that certificate to your app. You\u2019ll need to use the&nbsp;<em><a href=\"https:\/\/github.com\/SharePoint\/PnP-Partner-Pack\/blob\/master\/scripts\/Create-SelfSignedCertificate.ps1\" target=\"_blank\" rel=\"noreferrer noopener\">Create-SelfSignedCertificate.ps1<\/a><\/em>&nbsp;script for this step.<\/p>\n\n\n\n<p>The script below will generate a self-signed certificate using your app\u2019s name as its subject name, such as&nbsp;<em>Exo_V2_App.<\/em>&nbsp;The certificate will be valid for one (1) year.<\/p>\n\n\n\n<p>If you want to change the certificate\u2019s validity, you should change the\u00a0$certYears\u00a0value to the number of years you prefer. You may also change the\u00a0$certPassword\u00a0value if you want to use a different password for the resulting certificate (PFX) file.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>## Number of years of certificate validity\n$certYears = 1\n\n## Certificate (PFX) password\n$certPassword = '4~mt4G*8Qd@G'\n\n.\\Create-SelfSignedCertificate.ps1 -CommonName $appName `\n-StartDate (Get-Date).AddDays(-1) `\n-EndDate (Get-Date).AddYears($certYears) `\n-Password (ConvertTo-SecureString $certPassword -AsPlainText -Force) `\n-Force\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>When you run the code above in PowerShell, two files will be created, as you can see from the demo below.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/adamtheautomator.com\/wp-content\/uploads\/2021\/05\/Generate-Cert-min.gif\" alt=\"Generate a self-signed certificate\" class=\"wp-image-8619\"\/><figcaption>Generate a self-signed certificate<\/figcaption><\/figure>\n\n\n\n<p>The next step is to upload the certificate that you\u2019ve just created to your Azure AD app. The code below will locate the certificate (.CER) file in your working directory and then attach it to the Azure AD app. There\u2019s no need to modify the code, just copy and run it in PowerShell.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>## Get the certificate file (.CER)\n$CertificateFilePath = (Resolve-Path \".\\$($appName).cer\").Path\n## Create a new certificate object\n$cer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2\n$cer.Import(\"$($CertificateFilePath)\")\n$bin = $cer.GetRawCertData()\n$base64Value = &#91;System.Convert]::ToBase64String($bin)\n$bin = $cer.GetCertHash()\n$base64Thumbprint = &#91;System.Convert]::ToBase64String($bin)\n\n## Upload and assign the certificate to application in AzureAD\n$null = New-AzureADApplicationKeyCredential -ObjectId $myApp.ObjectID `\n-CustomKeyIdentifier $base64Thumbprint `\n-Type AsymmetricX509Cert -Usage Verify `\n-Value $base64Value `\n-StartDate ($cer.NotBefore) `\n-EndDate ($cer.NotAfter)\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>When you run the code above in PowerShell, you can expect to see no output, unless an error was encountered. The demo below shows the result when the code execution is successful.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/adamtheautomator.com\/wp-content\/uploads\/2020\/07\/attach-cert.gif\" alt=\"Attaching the certificate to the Azure AD app\" class=\"wp-image-3903\"\/><figcaption>Attaching the certificate to the Azure AD app<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-granting-admin-consent-to-the-application\">Granting Admin Consent to the Application<\/h3>\n\n\n\n<p>You\u2019re almost done with your set up. The next step is for a Global Admin to grant consent to your Azure AD app. This step can be executed by yourself or by another Global Admin in your organization.<\/p>\n\n\n\n<p>A Global admin can grant the consent from the&nbsp;<em><a href=\"https:\/\/aad.portal.azure.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Azure Active Directory admin center<\/a><\/em>. But, you can also just&nbsp;<em><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/manage-apps\/grant-admin-consent\" target=\"_blank\" rel=\"noreferrer noopener\">generate a consent URL<\/a><\/em>&nbsp;in PowerShell. Either give it to the Global admin, or you can use it yourself to grant consent.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>The consent URL follow this format below.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:&#47;&#47;login.microsoftonline.com\/{TenantID}\/adminconsent?client_id={ApplicationID}\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>The\u00a0{TenantID}\u00a0value is the directory ID or verified domain of your Office 365 tenant. The\u00a0{ApplicationID}\u00a0value is the AppID of the Azure AD application that you created previously.<\/p>\n\n\n\n<p>The code below will generate the consent URL based on the values stated above. The consent URL will then be displayed on the screen and launched using the computer\u2019s default browser.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>## Get the TenantID\n$tenantID = (Get-AzureADTenantDetail).ObjectID\n\n## Browse this URL\n$consentURL = \"https:\/\/login.microsoftonline.com\/$tenantID\/adminconsent?client_id=$($myApp.AppId)\"\n\n## Display the consent URL\n$consentURL\n\n## Launch the consent URL using the default browser\nStart-Process $consentURL\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>Refer to the demo below to see what happens when you run the code above in PowerShell.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/adamtheautomator.com\/wp-content\/uploads\/2020\/07\/grant-consent-2.gif\" alt=\"Generate consent URL and grant consent\" class=\"wp-image-3904\"\/><figcaption>Generate consent URL and grant consent<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-connecting-to-exchange-online-powershell\">Connecting to Exchange Online PowerShell<\/h2>\n\n\n\n<p>After creating the app and assigning the permission and role, you\u2019ll now need to upload and attach the certificate. You are now ready to connect to Exchange Online PowerShell using the app\u2019s certificate credentials.<\/p>\n\n\n\n<p>There are two ways to utilize the certificate credentials; using the local certificate file (.pfx), and using the thumbprint of the certificate installed in the&nbsp;<em><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows-hardware\/drivers\/install\/local-machine-and-current-user-certificate-stores\" target=\"_blank\" rel=\"noreferrer noopener\">current user\u2019s personal certificate store<\/a><\/em>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-authenticating-using-local-pfx-certificate\">Authenticating Using Local PFX Certificate<\/h3>\n\n\n\n<p>To connect to Exchange Online PowerShell using a local certificate to authenticate, you must have the following information:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>The Directory ID or verified domain of your Azure AD tenant.<\/li><li>The AppID of the application that you registered previously.<\/li><li>The full file path of the self-signed PFX certificate.<\/li><li>The password of the seld-sign PFX certificate.<\/li><\/ul>\n\n\n\n<p>Next, change the value of the\u00a0$tenantID,\u00a0$appID,\u00a0$CertificateFilePath, and\u00a0$pfxPassword\u00a0variables in the code below. Once you\u2019ve change the value of the variables as needed, copy the code and run it in PowerShell.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>## set the tenant ID (directory ID or domain)\n$tenantID = 'poshlab.ga'\n\n## Set the Exo_V2_App app id\n$appID = '3f76be04-5cf0-47f1-9df6-d05981a450fc'\n\n## Set the certificate file path (.pfx)\n$CertificateFilePath = 'C:\\exo_v2_demo\\Exo_V2_App.pfx'\n\n## Get the PFX password\n$pfxPassword = '4~mt4G*8Qd@G'\n\n## Connect to Exchange Online\nConnect-ExchangeOnline -CertificateFilePath $CertificateFilePath `\n-CertificatePassword (ConvertTo-SecureString -String $pfxPassword -AsPlainText -Force) `\n-AppID $appID `\n-Organization $tenantID\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>The demo below shows that the connecting to Exchange Online PowerShell si successful using the local certificate file authentication.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/adamtheautomator.com\/wp-content\/uploads\/2020\/07\/Connect-PFX.gif\" alt=\"Authenticating Using Local PFX Certificate\" class=\"wp-image-3905\"\/><figcaption>Authenticating Using Local PFX Certificate<\/figcaption><\/figure>\n\n\n\n<p>If you look closely at the code again, one glaring problem is that the pfx certificate password is visible. You may consider using some kind of&nbsp;<em><a href=\"https:\/\/www.sqlservercentral.com\/blogs\/good-bye-import-clixml-use-the-secrets-management-module-for-your-labs-and-demos\" target=\"_blank\" rel=\"noreferrer noopener\">secret management solution<\/a><\/em>&nbsp;to store the certificate credential for added security.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-authenticating-using-certificate-thumbprint\">Authenticating Using Certificate Thumbprint<\/h3>\n\n\n\n<p>This authentication method can be considered more secure than using the local certificate with a password. In this method, you will need to import the certificate to the Personal certificate store. You only need to use the thumbprint to identify which certificate to use for authentication.<\/p>\n\n\n\n<p>The first step is to import the PFX certificate into the Personal certificate store. Note that you only need to do this step once for the current user.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># CODE TO IMPORT THE PFX CERTIFICATE INTO THE CURRENT PERSONAL CERTIFICATE STORE\n## Set the certificate file path (.pfx)\n$CertificateFilePath = 'C:\\exo_v2_demo\\Exo_V2_App.pfx'\n\n## Get the PFX password\n$mypwd = Get-Credential -UserName 'Enter password below' -Message 'Enter password below'\n\n## Import the PFX certificate to the current user's personal certificate store.\nImport-PfxCertificate -FilePath $CertificateFilePath -CertStoreLocation Cert:\\CurrentUser\\My -Password $mypwd.Password\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>The demo below shows how to import the PFX certificate into the personal certificate store.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/adamtheautomator.com\/wp-content\/uploads\/2020\/07\/import-pfx.gif\" alt=\"Importing the PFX certificate\" class=\"wp-image-3906\"\/><figcaption>Importing the PFX certificate<\/figcaption><\/figure>\n\n\n\n<p>As you can see above, you will see the result of the PFX import process. Make sure to copy the value of the\u00a0Thumbprint\u00a0for quick reference later on.<\/p>\n\n\n\n<p>After importing the certificate, your scripts can now authenticate with Exchange Online PowerShell using its thumbprint.<\/p>\n\n\n\n<p>Edit the\u00a0$tenantID,\u00a0$appID, and\u00a0$CertificateThumbPrint\u00a0in the code below to match your correct values. Then, copy and run the code in PowerShell.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>## set the tenant ID (directory ID or domain)\n$tenantID = 'poshlab.ga'\n\n## Set the Exo_V2_App app id\n$appID = '3f76be04-5cf0-47f1-9df6-d05981a450fc'\n\n## Set the certificate thumbprint\n$CertificateThumbPrint = 'DED486B87C38CEA966EC71F8EE90BB3AAE694A74'\n\n## Connect to Exchange Online\nConnect-ExchangeOnline -CertificateThumbPrint $CertificateThumbPrint `\n-AppID $appID `\n-Organization $tenantID\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>Running the code above in PowerShell will give you an output similar to the demo below.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/adamtheautomator.com\/wp-content\/uploads\/2020\/07\/connect-store.gif\" alt=\"Output after running the code in Powershell\" class=\"wp-image-3907\"\/><figcaption>Output after running the code in Powershell<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-connecting-and-running-exchange-online-powershell-scripts-with-app-only-authentication\">Connecting and Running Exchange Online PowerShell Scripts with App-Only Authentication<\/h2>\n\n\n\n<p>So far, in this article, you\u2019ve only been copying and pasting code into PowerShell. But now that you\u2019re familiar with how app-only authentication works, you should apply it to run your PowerShell scripts.<\/p>\n\n\n\n<p>The script below connects to Exchange Online PowerShell using the certificate thumbprint to authenticate. Then, once connected, the script will get all the mailboxes available. The script is a saved in&nbsp;<em>C:\\exo_v2_demo\\ListExoMailbox.ps1<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>## Clean up Exchange Online Session\nGet-PSSession | Where-Object {$_.name -like \"ExchangeOnline*\"} | Remove-PSSession -ErrorAction SilentlyContinue\n\n## set the tenant ID (directory ID or domain)\n$tenantID = 'poshlab.ga'\n\n## Set the Exo_V2_App app id\n$appID = '3f76be04-5cf0-47f1-9df6-d05981a450fc'\n\n## Set the certificate thumbprint\n$CertificateThumbPrint = 'DED486B87C38CEA966EC71F8EE90BB3AAE694A74'\n\n## Connect to Exchange Online\nConnect-ExchangeOnline -CertificateThumbPrint $CertificateThumbPrint `\n-AppID $appID `\n-Organization $tenantID\n\n## Get All Mailbox\nWrite-Output \"Getting all mailboxes\"\nGet-Mailbox -ResultSize Unlimited | Format-Table Name,DisplayName\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>After you\u2019ve saved the script, run it in PowerShell. The script should connect to Exchange Online without any prompts and perform its function. Refer to the results shown in the demo below.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/adamtheautomator.com\/wp-content\/uploads\/2020\/07\/script-1.gif\" alt=\"PowerShell Script with App-Only Authentication\" class=\"wp-image-3908\"\/><figcaption>PowerShell Script with App-Only Authentication<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-summary\">Summary<\/h2>\n\n\n\n<p>The release of the EXO V2 PowerShell module is a welcome development. Knowing that Microsoft has decided to take away basic authentication for connecting to Exchange Online via PowerShell, and having this new app-only authentication feature allows admins to update their existing scripts.<\/p>\n\n\n\n<p>However, implementing EXO V2 app-only authentication is not without its challenges.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Using a local certificate file still requires a PFX password. If you can implement a\u00a0<em><a rel=\"noreferrer noopener\" href=\"https:\/\/www.powershellgallery.com\/packages\/BetterCredentials\" target=\"_blank\">credential<\/a><\/em>\u00a0or\u00a0<em><a rel=\"noreferrer noopener\" href=\"https:\/\/devblogs.microsoft.com\/powershell\/secrets-management-development-release\/\" target=\"_blank\">secret<\/a><\/em>\u00a0management strategy, you should get around this password exposure issue.<\/li><li>Using a certificate in the personal store is relatively more confident. But the certificate can only be accessed by the current user. So, if you\u2019ve\u00a0<em>set up a scheduled task<\/em>\u00a0to run the script using the credential of\u00a0<em>UserA<\/em>, then the certificate must be imported to the personal certificate store of\u00a0<em>UserA.<\/em><\/li><li>Certificates have expiration dates. This would mean that certificates need to be monitored, renewed, and re-attach it to the Azure AD app. Otherwise, the script will stop working due to authentication failure.<\/li><\/ul>\n\n\n\n<p>The benefits of using the new EXO V2 PowerShell module outweigh these challenges.<\/p>\n\n\n\n<p>In this article, you\u2019ve learned the step-by-step process of setting up the app-only authentication for the Exchange Online V2 PowerShell module. You\u2019ve also learned how to connect to Exchange Online PowerShell using a self-signed certificate.<\/p>\n\n\n\n<p>Now you do not have to deal with Exchange Online PowerShell MFA prompts and use app-only certificate-based authentication in your scripts.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this article, you will learn how to prepare to use the EXO V2 module to run Exchange Online unattended scripts with app-only modern authentication. You\u2019ll learn how to: Register a new app in Azure Active Directory and enable its\u00a0service principal. Assign API permissions and roles. Generate and upload a self-signed certificate. Use the app [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[69,35,58,62,73,59],"tags":[],"class_list":["post-3273","post","type-post","status-publish","format-standard","hentry","category-azure","category-cloud-computing","category-exchange-2013","category-microsoft-exchange-server-2016","category-exchange-server-2019","category-powershell"],"_links":{"self":[{"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=\/wp\/v2\/posts\/3273","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3273"}],"version-history":[{"count":11,"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=\/wp\/v2\/posts\/3273\/revisions"}],"predecessor-version":[{"id":3284,"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=\/wp\/v2\/posts\/3273\/revisions\/3284"}],"wp:attachment":[{"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3273"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3273"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3273"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}