Microsoft Teams utilization have phenomenally increased with the current COVID situation where almost everyone of us are working from the home. Microsoft Teams being one of the top collaboration software helping all of us to stay better connected during this time.
Most of the organizations doesn’t restrict the Team creation from Microsoft Teams because this factor is heavily influenced on better adoption rate of the Teams communication platform. The below script can be used to run in task scheduler or in Azure Function on a monthly basis for reviewing the Teams Created in last 30 days especially to see the Teams that have been archived and to see the fashion of teams created private or public by the users.
Below is the sample output of the script which will provide us the below details.
############################################################################################################################################
# Description :- Powershell Script To extract Teams Name,Owner,backup owner,owner count,member count,Group Type and Archive Status.
# Created Date :- 10-Oct-2020
# Created By :- Sathish Veerapandian
# Version :- 0.2
# Imp Notes :- Please ensure you have folder C:\Scripts and clear the output files generated every time when you run the script again.
# Info :- If you want to send reports as email please uncommentlast line and use the from/to address with SMTP Server
############################################################################################################################################
Connect-MicrosoftTeams
$Path="C:\scripts\TeamsReport.csv"
$Header = @"
TABLE {border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse;}<br />
TH {border-width: 1px; padding: 3px; border-style: solid; border-color: black; background-color: #48D1CC;}<br />
TD {border-width: 1px; padding: 3px; border-style: solid; border-color: black;background-color: #F0FFFF}<br />
"@
$Count = 0
Get-Team | foreach {
$TeamName = $null ; $TeamName = $_.DisplayName
$GroupId = $null ; $GroupId = $_.GroupId
$Visibility = $null ; $Visibility = $_.Visibility
$EmailAlias = $null; $EmailAlias = $_.MailNickName
$Archived = $null; $Archived = $_.Archived
$Count++
Write-Progress -Activity "`n Processed Teams count: $Count "`n" Currently Processing: $TeamName"
$TeamMembersCount = $null ; $TeamMembersCount = (Get-TeamUser -GroupId $GroupId).count
$TeamOwners = $null ; $TeamOwners = Get-TeamUser -GroupId $GroupId -role Owner
$TeamOwnersCount = $null ; $TeamOwnersCount = ($TeamOwners).count
$Owner1 = ""
$Owner2 = ""
If ($TeamOwnersCount -eq 1) { $Owner1 = $TeamOwners[0].User}
Elseif ($TeamOwnersCount -ge 2) { $Owner1 = $TeamOwners[0].User; $Owner2 = $TeamOwners[1].User}
$Output = [PSCustomObject]@{
TeamName = $Teamname
Owner1 = $Owner1
Owner2 = $Owner2
TeamOwnersCount = $TeamOwnersCount
TeamMembersCount = $TeamMembersCount
TeamEmailAlias = $EmailAlias
TeamVisibility = $Visibility
TeamArchiveStatus = $Archived
}
$Output | select * | Export-Csv $Path -NoTypeInformation -Append
$Data = Import-CSV "C:\scripts\TeamsReport.csv"
$data | ConvertTo-Html -Head $Header | Out-File -FilePath C:\Scripts\TeamsReport.html
# Send the exported html as email for evaluation
#Send-MailMessage -From senderemailID -To recipientemailid -Attachments "C:\Scripts\TeamsReport.html" -BodyAsHtml -SmtpServer mentionsmtpserver -Subject TeamsGroupReport
}
By using this article, we can start/stop VMs during off-business houses.This greatly benefits the customers especially in cost optimization and manual task overhead of performing this action manually. But we need to make sure that the VMs that we are selecting is present in the same subscription where the automation account and this schedule is created by selecting only the required VMs and excluding the other VMs.
Login to Azure portal
Go to ALL Services and Type Automation Account and Create Automation Account.
Under Process Automation, click Runbooks to open the list of runbooks.
Click on + Create a runbook button to create a new runbook
Provide and Name and Runbook Type as PowerShell for the new
runbook and then click Create button.
The Run Book is Empty, must deploy the Code then Save and Publish
Note: You can Find lot of powershell scripts for this task in the github and technet gallery. You can use the below one as well taken from github.
#PowerShell Script to Start and Stop VM's in Azure on Scheduled Intervals using Azure Automation Account#
Param(
[Parameter(Mandatory=$true,HelpMessage="Enter the value for Action. Values can be either stop or start")][String]$Action,
[Parameter(Mandatory=$false,HelpMessage="Enter the value for WhatIf. Values can be either true or false")][bool]$WhatIf = $false,
[Parameter(Mandatory=$false,HelpMessage="Enter the VMs separated by comma(,)")][string]$VMList
)
function ValidateVMList ($FilterVMList)
{
[boolean] $ISexists = $false
[string[]] $invalidvm=@()
$ExAzureVMList=@()
foreach($filtervm in $FilterVMList)
{
$currentVM = Get-AzureRmVM | where Name -Like $filtervm.Trim() -ErrorAction SilentlyContinue
if ($currentVM.Count -ge 1)
{
$ExAzureVMList+= @{Name = $currentVM.Name; Location = $currentVM.Location; ResourceGroupName = $currentVM.ResourceGroupName; Type = "ResourceManager"}
$ISexists = $true
}
elseif($ISexists -eq $false)
{
$invalidvm = $invalidvm+$filtervm
}
}
if($invalidvm -ne $null)
{
Write-Output "Runbook Execution Stopped! Invalid VM Name(s) in the VM list: $($invalidvm) "
Write-Warning "Runbook Execution Stopped! Invalid VM Name(s) in the VM list: $($invalidvm) "
exit
}
else
{
return $ExAzureVMList
}
}
function CheckExcludeVM ($FilterVMList)
{
[boolean] $ISexists = $false
[string[]] $invalidvm=@()
$ExAzureVMList=@()
<pre><code>foreach($filtervm in $FilterVMList)
{
$currentVM = Get-AzureRmVM | where Name -Like $filtervm.Trim() -ErrorAction SilentlyContinue
if ($currentVM.Count -ge 1)
{
$ExAzureVMList+=$currentVM.Name
$ISexists = $true
}
elseif($ISexists -eq $false)
{
$invalidvm = $invalidvm+$filtervm
}
}
if($invalidvm -ne $null)
{
Write-Output "Runbook Execution Stopped! Invalid VM Name(s) in the exclude list: $($invalidvm) "
Write-Warning "Runbook Execution Stopped! Invalid VM Name(s) in the exclude list: $($invalidvm) "
exit
}
else
{
Write-Output "Exclude VM's validation completed..."
} </code></pre>
}
<h1>-----L O G I N - A U T H E N T I C A T I O N-----</h1>
$connectionName = "AzureRunAsConnection"
try
{
# Get the connection "AzureRunAsConnection "
$servicePrincipalConnection=Get-AutomationConnection -Name $connectionName
<pre><code>"Logging in to Azure..."
Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint </code></pre>
}
catch
{
if (!$servicePrincipalConnection)
{
$ErrorMessage = "Connection $connectionName not found."
throw $ErrorMessage
} else{
Write-Error -Message $_.Exception
throw $_.Exception
}
}
<h1>---------Read all the input variables---------------</h1>
$StartResourceGroupNames = Get-AutomationVariable -Name 'RG-Of-VMs-To-Start'
$StopResourceGroupNames = Get-AutomationVariable -Name 'RG-Of-VMs-To-Stop'
$ExcludeVMNames = Get-AutomationVariable -Name 'Excluded-List-Of-VMs'
try
{
$Action = $Action.Trim().ToLower()
<pre><code> if(!($Action -eq "start" -or $Action -eq "stop"))
{
Write-Output "`$Action parameter value is : $($Action). Value should be either start or stop."
Write-Output "Completed the runbook execution..."
exit
}
Write-Output "Runbook Execution Started..."
[string[]] $VMfilterList = $ExcludeVMNames -split ","
#If user gives the VM list with comma seperated....
$AzurVMlist = Get-AutomationVariable -Name $VMList
if(($AzurVMlist))
{
Write-Output "list of all VMs = $($AzurVMlist)"
[string[]] $AzVMList = $AzurVMlist -split ","
}
if($Action -eq "stop")
{
Write-Output "List of all ResourceGroups = $($StopResourceGroupNames)"
[string[]] $VMRGList = $StopResourceGroupNames -split ","
}
if($Action -eq "start")
{
Write-Output "List of all ResourceGroups = $($StopResourceGroupNames)"
[string[]] $VMRGList = $StartResourceGroupNames -split ","
}
#Validate the Exclude List VM's and stop the execution if the list contains any invalid VM
if (([string]::IsNullOrEmpty($ExcludeVMNames) -ne $true) -and ($ExcludeVMNames -ne "none"))
{
Write-Output "Values exist on the VM's Exclude list. Checking resources against this list..."
CheckExcludeVM -FilterVMList $VMfilterList
}
$AzureVMListTemp = $null
$AzureVMList=@()
if ($AzVMList -ne $null)
{
##Action to be taken based on VM List and not on Resource group.
##Validating the VM List.
Write-Output "VM List is given to take action (Exclude list will be ignored)..."
$AzureVMList = ValidateVMList -FilterVMList $AzVMList
}
else
{
##Getting VM Details based on RG List or Subscription
if (($VMRGList -ne $null) -and ($VMRGList -ne "*"))
{
foreach($Resource in $VMRGList)
{
Write-Output "Validating the resource group name ($($Resource))"
$checkRGname = Get-AzureRmResourceGroup -Name $Resource.Trim() -ev notPresent -ea 0
if ($checkRGname -eq $null)
{
Write-Warning "$($Resource) is not a valid Resource Group Name. Please verify your input"
}
else
{
# Get resource manager VM resources in group and record target state for each in table
$taggedRMVMs = Get-AzureRmVM | ? { $_.ResourceGroupName -eq $Resource}
foreach($vmResource in $taggedRMVMs)
{
if ($vmResource.ResourceGroupName -Like $Resource)
{
$AzureVMList += @{Name = $vmResource.Name; ResourceGroupName = $vmResource.ResourceGroupName; Type = "ResourceManager"}
}
}
}
}
}
else
{
Write-Output "Getting all the VM's from the subscription..."
$ResourceGroups = Get-AzureRmResourceGroup
foreach ($ResourceGroup in $ResourceGroups)
{
# Get resource manager VM resources in group and record target state for each in table
$taggedRMVMs = Get-AzureRmVM | ? { $_.ResourceGroupName -eq $ResourceGroup.ResourceGroupName}
foreach($vmResource in $taggedRMVMs)
{
Write-Output "RG : $($vmResource.ResourceGroupName) , ARM VM $($vmResource.Name)"
$AzureVMList += @{Name = $vmResource.Name; ResourceGroupName = $vmResource.ResourceGroupName; Type = "ResourceManager"}
}
}
}
}
$ActualAzureVMList=@()
if($AzVMList -ne $null)
{
$ActualAzureVMList = $AzureVMList
}
#Check if exclude vm list has wildcard
elseif(($VMfilterList -ne $null) -and ($VMfilterList -ne "none"))
{
$ExAzureVMList = ValidateVMList -FilterVMList $VMfilterList
foreach($VM in $AzureVMList)
{
##Checking Vm in excluded list
if($ExAzureVMList.Name -notcontains ($($VM.Name)))
{
$ActualAzureVMList+=$VM
}
}
}
else
{
$ActualAzureVMList = $AzureVMList
}
Write-Output "The current action is $($Action)"
$ActualVMListOutput=@()
if($WhatIf -eq $false)
{
$AzureVMListARM=@()
# Store the ARM VM's
$AzureVMListARM = $ActualAzureVMList | Where-Object {$_.Type -eq "ResourceManager"}
# process the ARM VM's
if($AzureVMListARM -ne $null)
{
foreach($VM in $AzureVMListARM)
{
$ActualVMListOutput = $ActualVMListOutput + $VM.Name + " "
#ScheduleSnoozeAction -VMObject $VM -Action $Action
#------------------------
try
{
Write-Output "VM action is : $($Action)"
Write-OutPut $VM.ResourceGroupName
$VMState = Get-AzureRmVM -ResourceGroupName $VM.ResourceGroupName -Name $VM.Name -status | Where-Object { $_.Name -eq $VM.Name }
if ($Action.Trim().ToLower() -eq "stop")
{
Write-Output "Stopping the VM : $($VM.Name)"
Write-Output "Resource Group is : $($VM.ResourceGroupName)"
if($VMState.Statuses[1].DisplayStatus -ne "VM running")
{
Write-Output "VM is not in running state, No actions performed"
}
else
{
$Status = Stop-AzureRmVM -Name $VM.Name -ResourceGroupName $VM.ResourceGroupName -Force
if($Status -eq $null)
{
Write-Output "Error occured while stopping the Virtual Machine."
}
else
{
Write-Output "Successfully stopped the VM $($VM.Name)"
}
}
}
elseif($Action.Trim().ToLower() -eq "start")
{
Write-Output "Starting the VM : $($VM.Name)"
Write-Output "Resource Group is : $($VM.ResourceGroupName)"
if($VMState.Statuses[1].DisplayStatus -eq "VM running")
{
Write-Output "VM already Running, No actions performed"
}
else
{
$Status = Start-AzureRmVM -Name $VM.Name -ResourceGroupName $VM.ResourceGroupName
if($Status -eq $null)
{
Write-Output "Error occured while starting the Virtual Machine $($VM.Name)"
}
else
{
Write-Output "Successfully started the VM $($VM.Name)"
}
}
}
}
catch
{
Write-Output "Error Occurred..."
Write-Output $_.Exception
}
#--------------------------
}
Write-Output "Completed the $($Action) action on the following ARM VMs: $($ActualVMListOutput)"
}
}
elseif($WhatIf -eq $true)
{
Write-Output "WhatIf parameter is set to True..."
Write-Output "When 'WhatIf' is set to TRUE, runbook provides a list of Azure Resources (e.g. VMs), that will be impacted if you choose to deploy this runbook."
Write-Output "No action will be taken at this time..."
Write-Output $($ActualAzureVMList)
}
Write-Output "Runbook Execution Completed..."
}
catch
{
$ex = $_.Exception
Write-Output $_.Exception
}
Now in Automation Account, under Shared Resources click Variables and add these variables [Note: while creating the variables you have to provide some Value, later you can delete the value if required]
Explanation
of these variables-
RG-Of-VMs-To-Start: ResourceGroup
that contains VMs to be started. Must be in the same subscription that the
Azure Automation Run-As account has permission to manage.
RG-Of-VMs-To-Stop:
ResourceGroup that contains VMs to be stopped. Must be in the
same subscription that the Azure Automation Run-As account has permission to
manage.
Excluded-List-Of-VMs:
VM
names to be excluded from being started/Stopped
Note: These Variables are defined in the PowerShell Code and should
not be changed if used by default.
VMstoStartStop: Provide
the list of the VMs to be started or stopped.
Now we will create schedule for the start & stop of VMs. Under Shared Resources, click on Schedules and then click + Add a schedule and provide a Name and time and recurrence frequency for the schedule and Click Create
Similarly create schedule for Stop_VM
Now we will attach these schedules with the runbook. Go to Runbook and then open the runbook, click schedules and then + Add a schedule
Now link Start_VM and then provide parameter values. ACTION can have values like start or stop. In VMLIST provide the variable name “VMstoStartStop” which contains the VM names. Click create
Similarly attach the Stop_VM and provide the Action value Stop and VMList value VMstoStartstop.
So now we have two schedules start-vm and stop-vm which would be running on the defined schedules.
Now your runbook will execute the start of VM and stop of VM as per the schedule attached. The results of runbook execution can be seen under Process Automation –> Jobs.
This task greatly benefits the hassle of maintaining and performing this task manually from the admin side.
When a user with MFA enabled loses his mobile phone then he wouldn’t be able to login to new devices or in the old devices where the token life time have expired.
Currently in this scenario the user have to report to help desk team. Unfortunately only the global admins can perform the force reset of MFA account for the user to reset his Strongauthenticationmethods value to null to clear the old lost device.
There is a work around which can be used until we get a delegated RBAC role for performing this action. With Azure Automation account, creating a flow, integrating with flow and delegating this action to helpdesk admins will reduce the load on global admins performing this action.
Prerequisites:
Create New Automation Accounts from azure portal. Azure subscription required.They provide 500 minutes free every month.
Create new Work Flow from global admin account.This action needs to be performed from global admin account.
Enter the Global admin Credentials in the created automation account. Very Important that this account used to execute must not have MFA enabled.
Now we need to create the flow from the global admin account to execute this action.
Head over to Flow (https://flow.microsoft.com ) and provision a new personal Flow. Click new flow – Click Create from Blank.
Choose – Flow Button for Mobile , Flow Button for Mobile – manually trigger a Flow , Select AA- Type useremail as input flow.
Navigate to triggers – Select Manually trigger a flow.
Type UserEmail as input flow-Click on New Step – Add an Action
Click on Choose an action – Select Azure Automation – Create a Job – Provide the required credentials and subscription details.
Provide the required credentials and subscription details.
This part is very important we need to select the input as UserEmail as below. This parameter is required for the run book to execute the operation.After that we can see that the RunBook Parameter is UserEmail.
Now we will see the flow is connected to Azure automation account
Now Navigate to My Flows- Select the new flow – Click on – Run Now
We can see the flow will be successfully started and execute the requested operation of resetting the MFA value to null for the user.
We can run them on automation accounts and see them for verification and they will be successful.
From the global admin Flow login – Delegate this flow to helpdesk admins as manage run only user permission.
The actual operation is executed by the global admin account however the helpdesk team will be triggering this action through the delegated run only permissions assigned to them in created Microsoft flow.