Welcome to my first ever open-source project!
This project allows you to implement the required services and activate a system that will run your EOP configuration and match it with Microsofts best practice once a month.
The Microsoft Defender for Office 365 Recommended Configuration Analyzer (ORCA)
You may want to keep up to date with the everchanging best practices from Microsoft when it comes to security without giving out Security Reader/Admin permissions
Security best practices change all the time and this way you can get an update monthly (or more frequently) and ensure you are inline with the recommendations.
Merger or aquisitions has happened and you onboard the domain of the new company in to your Microsoft 365 tenant as an accepted domin, but you forget to add them to policies and configure DKIM, this way you are alerted of this.
At the time of developing this solution the ExchangeOnlineManagement module had just announced their release of the -ManagedIdentity switch. The switch was limited to only work on Azure VM or Azure VM scaleset machines.
When trying to call this command with the switch locally you would just receive a timeout, same for Azure Functions.
The ORCA module has network-dependencies and uses the Resolve-DNSName commandlet. This is not allowed to run inside Azure Automation sandboxes, so we could not run the automation.ps1 script inside a Runbook.
There is one automation account used and that is to power on and off the virtual machine which in this case will simulate the Runbook sandbox VM.
The VM will be powered off at all times except for a few hours every month to keep costs down.
We use Twilio Sendgrid Email API services because this is a service Microsoft has partnership with and you can configure a free subscription to their services directly in the Azure Portal.
This is what will allow us to send email in automated powershell-scripts.
Resources being configured:
Virtual machine with Managed Identity
Azure Automation account
Keyvault
Twilio SaaS subscription (Free plan)
All resources gets a tagged applied so you can track what resources are dependencies to this solution
Note that all the commands you will be instructed to execute will assume you are standing in the git-directory when executing them.
az login
az account list -o table
az account set -s "<Subscription Name>"
Creata a new resource group (if you are using an existing one skip this command)
az group create --name <name of RG> --location <location>
It is always a good idea to check for upgrades for Bicep before deploying templates
az bicep upgrade
The git repository contains a folder with bicep templates that will provision some resources required for the solution to work
Important: Go through all the parameters in deploy/params.json file and update them to suit your setup
In your shell run the following command to deploy the resources after updating params.json
az deployment group create -g <name of RG> --template-file deploy/main.bicep --parameters deploy/params.json
Enter the password for the virtual machine admin account, IMPORTANT: document this somewhere safe
Wait for the deployment to finish, if you wish you can visit the resource group in the portal and click on deployments to follow all items being deployed or just wait for the terminal to finish
Microsoft recommends using Twilio for sending emails so we should be able to trust that they are a reliant partner. They even let you register for their services through the Azure Portal
Remember that you need a mailbox that you can access which will be used for the setup, example: automation@domain.com
Source-reference from MS Docs: Send an Email with Automation
Visit the manual from Twilio for setup, ignore the part with creating subscription and resource groups as we have those already, Twilio email setup manual
Create a Twilio Sendgrid Account
Create the API-key
Create a single sender
Note: We use Single Sender Verification when performing the first-time setup, which is recommended for proof of concepts/test-setups.
After you have verified the entire setup for this solution you can look into Domain Verification
In this step you will store the API-key created in the previous step in the Keyvault you have deployed.
The script will use the keyvault to retreive the API-key for sending email and the VM requires Exchange Administrator to run the powershell commandlets Connect-ExchangeOnline
& Invoke-ORCAReport
Go to the keyvault and to access policies
Run the Register-KeyVaultSecret.ps1 script in Powershell on your local machine
$SendGridAPIKey = "<Paste your API code here>"
$VaultName = "<Enter keyvault name here>"
Connect-AzAccount #Login to Azure through Powershell
$Secret = ConvertTo-SecureString -String $SendGridAPIKey ` -AsPlainText -Force
Set-AzKeyVaultSecret -VaultName $VaultName -Name 'SendGridAPIKey'
-SecretValue $Secret
The managed identity needs certain permissions in order to authenticate and query Exchange Online, we will use MS Graph for this
```Powershell
Connect-MgGraph -Scopes "User.Read.all","Application.Read.All","AppRoleAssignment.ReadWrite.All"
$params = @{
ServicePrincipalId = '<Enter managed object ID of Virtual Machine>' # managed identity object id - you can find this under your resource group in the Azure Portal -> deployments -> vmDeployment -> outputs
PrincipalId = '<Enter managed object ID of Virtual Machine>' # managed identity object id - you can find this under your resource group in the Azure Portal -> deployments -> vmDeployment -> outputs
ResourceId = (Get-MgServicePrincipal -Filter "AppId eq '00000002-0000-0ff1-ce00-000000000000'").id # Exchange online
AppRoleId = "dc50a0fb-09a3-484d-be87-e023b12c6440" # Exchange.ManageAsApp
}
New-MgServicePrincipalAppRoleAssignedTo @params
We also need to assign the Exchange Administrator role to the Managed Identity as only the application permissions are not supported.
It may take 10-15 minutes for all the permissions to propagate.
Note: In theory it should be enough with either the MS-graph permissions or the Exchange Admin role but at the time of developing this solution I have had issues where I did not complete both, this requirement may change later.
In the future the aim is to deliver a solution that will not require this step, but for now we do.
Login to the VM through RDP
On the desktop create a file called: Trigger-ORCA.xml (You may need to show file extensions to ensure its .xml and not .txt)
Paste in the following code:
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>2022-10-25T08:03:23.4153238</Date>
<Author>machine\admin</Author>
<Description>Triggers and sends the automated ORCA report</Description>
<URI>\Trigger-ORCAReport</URI>
</RegistrationInfo>
<Triggers>
<BootTrigger>
<Enabled>true</Enabled>
</BootTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<UserId>S-1-5-21-2123838175-1840825630-523378789-500</UserId>
<LogonType>Password</LogonType>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession>
<UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT72H</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>powershell.exe</Command>
<Arguments>-ExecutionPolicy Bypass -File "C:\Automation\ORCA\Trigger-ORCAReport.ps1"</Arguments>
</Exec>
</Actions>
</Task>
Open task scheduler and use Import task...
Click Change user or group
Click OK and enter the password for the account orcaadmin
Enter the following command in Powershell
New-Item -Path "C:\Automation\ORCA\Trigger-ORCAReport.ps1" -Force
Open the Trigger-ORCAReport.ps1 through powershell and paste in the code from automation.ps1 that you received from cloning this repo
Open Powershell as an Administrator on the Virtual Machine and run the following code:
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Install-PackageProvider -Name NuGet -Confirm:$false -Force
Install-Module NuGet -Confirm:$false -Force
Install-Module ExchangeOnlineManagement -Confirm:$false -Force
Install-Module ORCA -Confirm:$false -Force
Install-Module Az -Confirm:$false -Force
Wait for the script to complete, this will install all required modules for Trigger-ORCAReport.ps1 to run successfully
After the modules are installed you should be ready to try a test-run of Trigger-ORCAReport.ps1
If successful it should authenticate to Exchange Online, trigger the ORCA-report & send the document to you in an email.
When you deployed all the Azure resources in step three you configured an Automation Account, this is what we will use to schedule the VM stop and start.
[CmdletBinding()]
param (
[string] $vmName = "<enter VM name here>",
[string] $resourceGroupName = "<Enter the RG you are working with here>"
)
$ProgressPreference="silentlyContinue"
Disable-AzContextAutosave -Scope Process
$AzureContext = (Connect-AzAccount -Identity).context
$AzureContext = Set-AzContext -SubscriptionName $AzureContext.Subscription -DefaultProfile $AzureContext
Start-AzVM -ResourceGroupName $resourceGroupName -Name $vmName -Confirm:$false
- Choose **Save** then **Publish**
- Choose **Link to schedule**
- Choose **Link a scheduled to your runbook**
- Choose **Add a schedule**
- Give it a name: **Start ORCA**
- Starts: Chose the first of following month (We are assuming you will run this on the 1st of every month, you can adjust to fit you)
- 8:00 AM for time
- Set your Timezone
- Chose **Recurring** -> **Recur every** 1 month
- Chose **Month days** -> 1
- Leave everything else as default and choose **Create**
- Choose **Parameters and run settings**
- Change nothing and just choose **OK**
- Choose **OK**
- Go back to automation accounts and choose **Stop-VM**
- Repeat the same steps and paste the following code
```Powershell
[CmdletBinding()]
param (
[string] $vmName = "<Enter your VM name here>",
[string] $resourceGroupName = "<Enter your RG here>"
)
$ProgressPreference="silentlyContinue"
Disable-AzContextAutosave -Scope Process
# Connect to Azure with system-assigned managed identity
$AzureContext = (Connect-AzAccount -Identity).context
# set and store context
$AzureContext = Set-AzContext -SubscriptionName $AzureContext.Subscription -DefaultProfile $AzureContext
# Stop the VM
Stop-AzVM -ResourceGroupName $resourceGroupName -Name $vmName -Confirm:$false -Force
Repeat the steps you did for linking to a schedule, just chose a time that is later then whatever time you set for Start-VM
In my use case the Trigger-ORCAReport.ps1 runs for a few hours because of the amount of custom domains in the customer tenant
This means my time to trigger Stop-VM will take place a few hours later
Once you have published and linked both runbooks to a schedule you can test the runbooks to make sure it works to stop and start the virtual machine.
If you have followed along so far this means your VM is currently running.
Go ahead and test Stop-VM runbook
When you have clicked in on Stop-VM runbook you can click Start
If you don't run into any issues you can go ahead and test Start-VM runbook
If successful and you receive the report go ahead and turn off the VM any way you see fit