ERROR: The specified module 'ExchangeOnlineManagement' was not loaded because no valid module file was found in any module directory

john john Pter 1,065 Reputation points
2025-05-08T22:15:26.1166667+00:00

I have created an Azure function under consumption plan, that uses PowerShell, then inside the run.ps1, i added this code, to remove a user from all the groups assigned to the user:-


param($Timer)
Import-Module ExchangeOnlineManagement
Import-Module Microsoft.Graph
# Connect to Microsoft Graph with required delegated scopes
 Write-Host "Start"
Connect-MgGraph -Scopes "User.Read.All", "Group.Read.All", "GroupMember.ReadWrite.All", "Directory.Read.All", "Group.ReadWrite.All" -Identity
 Write-Host "Connect to Graph"
# Connect to Exchange Online
Connect-ExchangeOnline -Identity

# Target user email
$userEmail = "test@***.onmicrosoft.com" 

# --- PART 1: Azure AD + Office 365 Groups ---
$userList = Get-MgUser -Filter "mail eq '$userEmail' or userPrincipalName eq '$userEmail'"  -ConsistencyLevel eventual -All -Property Id, DisplayName, UserPrincipalName, Mail

$userObj = $userList | Select-Object -First 1

if (-not $userObj) {
    Write-Host "User $userEmail not found in Microsoft Graph. Skipping AAD removal."
} elseif (-not $userObj.Id) {
    Write-Host "User objectId missing even after lookup. Skipping AAD removal."
} else {
    Write-Host "Found user: $($userObj.DisplayName) ($($userObj.UserPrincipalName))"

    $groups = Get-MgUserMemberOf -UserId $userObj.Id -All
    $groupsAsOwner = Get-MgUserOwnedObject -UserId $userObj.Id -All


    foreach ($group in $groups) {
        if ($group.AdditionalProperties.'@odata.type' -ne '#microsoft.graph.group') {
            continue
        }

        $groupId = $group.Id
        $groupName = $group.AdditionalProperties.DisplayName

        if (-not $groupId) {
            Write-Host "Skipping group with empty ID"
            continue
        }

        $fullGroup = Get-MgGroup -GroupId $groupId

        if ($fullGroup.GroupTypes -contains "DynamicMembership") {
            Write-Host "Skipping dynamic group: ${groupName}"
            continue
        }

        # Remove as MEMBER
        try {
            Remove-MgGroupMemberByRef -GroupId $groupId -DirectoryObjectId $userObj.Id -ErrorAction Stop
            Write-Host "Removed as MEMBER from ${groupName}"
        }
        catch {
            Write-Host "Failed MEMBER removal from ${groupName}: $($_.Exception.Message)"
        }}
foreach ($group2 in $groupsAsOwner) {
$groupId2 = $group2.Id
$groupName2 = $group2.AdditionalProperties.DisplayName
        # Remove as OWNER
        try {
            Remove-MgGroupOwnerByRef -GroupId $groupId2 -DirectoryObjectId $userObj.Id -ErrorAction Stop
            Write-Host "Removed as OWNER from ${groupName2}"
        }
        catch {
            Write-Host "Failed OWNER removal from ${groupName2}: $($_.Exception.Message)"
        }
    }
    }

# --- PART 2: Exchange Distribution Groups + Mail-enabled Security Groups ---
$dlGroups = Get-DistributionGroup -ResultSize Unlimited

foreach ($dl in $dlGroups) {
    try {
        # Remove as MEMBER
        $members = Get-DistributionGroupMember -Identity $dl.Identity -ResultSize Unlimited
        $memberMatch = $members | Where-Object { $_.PrimarySmtpAddress -ieq $userEmail }

        if ($memberMatch) {
            Remove-DistributionGroupMember -Identity $dl.Identity -Member $userEmail -BypassSecurityGroupManagerCheck -Confirm:$false
            Write-Host "Removed as MEMBER from Exchange group: $($dl.DisplayName)"
        }

        # Remove as OWNER
        # $owners = (Get-DistributionGroup -Identity $dl.Identity).ManagedBy
        $owners = (Get-DistributionGroup -Identity $dl.Identity).ManagedBy | Foreach-Object { Get-Mailbox $_ }
        $ownerMatch = $owners | Where-Object { $_.PrimarySmtpAddress -ieq $userEmail }

        if ($ownerMatch) {
            Set-DistributionGroup -Identity $dl.Identity -ManagedBy @{ Remove = "$userEmail" }
            Write-Host "Removed as OWNER from Exchange group: $($dl.DisplayName)"
        }
    }
    catch {
        Write-Host "Failed Exchange removal for group: $($dl.DisplayName) - $($_.Exception.Message)"
    }
}

Write-Host "Cleanup script completed."

and we have the following inside the function.json:-


{
  "bindings": [
    {
      "name": "Timer",
      "type": "timerTrigger",
      "direction": "in",
      "schedule": "0 */5 * * * *"
    }
  ]
}
 

and the following inside the requirments.psd1 to load the external libraries:-

    
@{
    'ExchangeOnlineManagement' = '3.*'
    'Microsoft.Graph' = '2.*'
}

as follow:-

pB3Trz3f

but when i run the script, i got those errors:-


       2025-05-08T22:03:06Z   [Error]   ERROR: The term 'Disable-AzContextAutosave' is not recognized as a name of a cmdlet, function, script file, or executable program.
        Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
        
        Exception             : 
            Type        : System.Management.Automation.CommandNotFoundException
            ErrorRecord : 
                Exception             : 
                    Type    : System.Management.Automation.ParentContainsErrorRecordException
                    Message : The term 'Disable-AzContextAutosave' is not recognized as a name of a cmdlet, function, script file, or executable program.
                              Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
                    HResult : -2146233087
                TargetObject          : Disable-AzContextAutosave
                CategoryInfo          : ObjectNotFound: (Disable-AzContextAutosave:String) [], ParentContainsErrorRecordException
                FullyQualifiedErrorId : CommandNotFoundException
                InvocationInfo        : 
                ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\profile.ps1: line 15
            CommandName : Disable-AzContextAutosave
            TargetSite  : 
                Name          : LookupCommandInfo
                DeclaringType : [System.Management.Automation.CommandDiscovery]
                MemberType    : Method
                Module        : System.Management.Automation.dll
            Message     : The term 'Disable-AzContextAutosave' is not recognized as a name of a cmdlet, function, script file, or executable program.
                          Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
            Data        : System.Collections.ListDictionaryInternal
            Source      : System.Management.Automation
            HResult     : -2146233087
            StackTrace  : 
        TargetObject          : Disable-AzContextAutosave
        CategoryInfo          : ObjectNotFound: (Disable-AzContextAutosave:String) [], CommandNotFoundException
        FullyQualifiedErrorId : CommandNotFoundException
            ScriptName       : C:\home\site\wwwroot\profile.ps1
            Line             : Disable-AzContextAutosave -Scope Process | Out-Null
                               
            Statement        : Disable-AzContextAutosave
        ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\profile.ps1: line 15
        
        2025-05-08T22:03:06Z   [Error]   ERROR: The term 'Connect-AzAccount' is not recognized as a name of a cmdlet, function, script file, or executable program.
        Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
System.Management.Automation.CommandNotFoundExceptionSystem.Management.Automation.ParentContainsErrorRecordException
                    Message : The term 'Connect-AzAccount' is not recognized as a name of a cmdlet, function, script file, or executable program.
                              Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
                    HResult : -2146233087
                TargetObject          : Connect-AzAccount
                CategoryInfo          : ObjectNotFound: (Connect-AzAccount:String) [], ParentContainsErrorRecordException
                FullyQualifiedErrorId : CommandNotFoundException
                InvocationInfo        : 
        
                ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\profile.ps1: line 16
            CommandName : Connect-AzAccount
            TargetSite  : 
                Name          : LookupCommandInfo
                DeclaringType : [System.Management.Automation.CommandDiscovery]
                MemberType    : Method
                Module        : System.Management.Automation.dll
            Message     : The term 'Connect-AzAccount' is not recognized as a name of a cmdlet, function, script file, or executable program.
                          Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
            Data        : System.Collections.ListDictionaryInternal
            Source      : System.Management.Automation
            HResult     : -2146233087
            StackTrace  : 
        TargetObject          : Connect-AzAccount
        CategoryInfo          : ObjectNotFound: (Connect-AzAccount:String) [], CommandNotFoundException
        FullyQualifiedErrorId : CommandNotFoundException
        InvocationInfo        : 
            PositionMessage  : At C:\home\site\wwwroot\profile.ps1:16 char:5
                               +     Connect-AzAccount -Identity
                               +     ~~~~~~~~~~~~~~~~~
        2025-05-08T22:03:06Z   [Error]   Errors reported while executing profile.ps1. See logs for detailed errors. Profile location: C:\home\site\wwwroot\profile.ps1.
        2025-05-08T22:03:07Z   [Error]   ERROR: The specified module 'ExchangeOnlineManagement' was not loaded because no valid module file was found in any module directory.
        
        Exception             : 
            Type    : System.IO.FileNotFoundException
            Message : The specified module 'ExchangeOnlineManagement' was not loaded because no valid module file was found in any module directory.
            HResult : -2147024894
        TargetObject          : ExchangeOnlineManagement
        CategoryInfo          : ResourceUnavailable: (ExchangeOnlineManagement:String) [Import-Module], FileNotFoundException
        FullyQualifiedErrorId : Modules_ModuleNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand
        InvocationInfo        : 
            MyCommand        : Import-Module
        ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\TimerTrigger1\run.ps1: line 2
        PipelineIterationInfo : 
        2025-05-08T22:03:07Z   [Error]   ERROR: The specified module 'Microsoft.Graph' was not loaded because no valid module file was found in any module directory.
        
        Exception             : 
            Type    : System.IO.FileNotFoundException
            Message : The specified module 'Microsoft.Graph' was not loaded because no valid module file was found in any module directory.
            HResult : -2147024894
        TargetObject          : Microsoft.Graph
        CategoryInfo          : ResourceUnavailable: (Microsoft.Graph:String) [Import-Module], FileNotFoundException
        FullyQualifiedErrorId : Modules_ModuleNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand
        InvocationInfo        : 
        
        ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\TimerTrigger1\run.ps1: line 3
        PipelineIterationInfo : 
        2025-05-08T22:03:07Z   [Error]   ERROR: The term 'Connect-MgGraph' is not recognized as a name of a cmdlet, function, script file, or executable program.
        Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
System.Management.Automation.CommandNotFoundException
System.Management.Automation.ParentContainsErrorRecordException
                    Message : The term 'Connect-MgGraph' is not recognized as a name of a cmdlet, function, script file, or executable program.
                              Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
                    HResult : -2146233087
                TargetObject          : Connect-MgGraph
                CategoryInfo          : ObjectNotFound: (Connect-MgGraph:String) [], ParentContainsErrorRecordException
                FullyQualifiedErrorId : CommandNotFoundException
                InvocationInfo        : 
                ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\TimerTrigger1\run.ps1: line 6
            CommandName : Connect-MgGraph
            TargetSite  : 
                Name          : LookupCommandInfo
                DeclaringType : [System.Management.Automation.CommandDiscovery]
                MemberType    : Method
                Module        : System.Management.Automation.dll
            Message     : The term 'Connect-MgGraph' is not recognized as a name of a cmdlet, function, script file, or executable program.
                          Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
            Data        : System.Collections.ListDictionaryInternal
            Source      : System.Management.Automation
            HResult     : -2146233087
        TargetObject          : Connect-MgGraph
        CategoryInfo          : ObjectNotFound: (Connect-MgGraph:String) [], CommandNotFoundException
        FullyQualifiedErrorId : CommandNotFoundException
        InvocationInfo        : 
        ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\TimerTrigger1\run.ps1: line 6
        
        2025-05-08T22:03:07Z   [Error]   ERROR: The term 'Connect-ExchangeOnline' is not recognized as a name of a cmdlet, function, script file, or executable program.
        Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
        
        Exception             : 
            Type        : System.Management.Automation.CommandNotFoundException
            ErrorRecord : 
                Exception             : 
                    Type    : System.Management.Automation.ParentContainsErrorRecordException
                    Message : The term 'Connect-ExchangeOnline' is not recognized as a name of a cmdlet, function, script file, or executable program.
                              Check the spelling of the name, or if a path was included, verify that the path is correct and try again.C:\home\site\wwwroot\TimerTrigger1\run.ps1: line 9
            CommandName : Connect-ExchangeOnline
            TargetSite  : 
                Name          : LookupCommandInfo
                DeclaringType : [System.Management.Automation.CommandDiscovery]
                MemberType    : Method
                Module        : System.Management.Automation.dll
            Message     : The term 'Connect-ExchangeOnline' is not recognized as a name of a cmdlet, function, script file, or executable program.
                          Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
            Data        : System.Collections.ListDictionaryInternal
            Source      : System.Management.Automation
            HResult     : -2146233087
        TargetObject          : Connect-ExchangeOnline
        CategoryInfo          : ObjectNotFound: (Connect-ExchangeOnline:String) [], CommandNotFoundException
        FullyQualifiedErrorId : CommandNotFoundExceptionC:\home\site\wwwroot\TimerTrigger1\run.ps1
            Line             : Connect-ExchangeOnline -Identity
                               
            Statement        : Connect-ExchangeOnline
            PositionMessage  : At C:\home\site\wwwroot\TimerTrigger1\run.ps1:9 char:1
                               + Connect-ExchangeOnline -Identity
                               + ~~~~~~~~~~~~~~~~~~~~~~
            PSScriptRoot     : C:\home\site\wwwroot\TimerTrigger1
            PSCommandPath    : C:\home\site\wwwroot\TimerTrigger1\run.ps1
            InvocationName   : Connect-ExchangeOnline
            CommandOrigin    : Internal
        ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\TimerTrigger1\run.ps1: line 9
        
        2025-05-08T22:03:07Z   [Error]   ERROR: The term 'Get-MgUser' is not recognized as a name of a cmdlet, function, script file, or executable program.
        Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
        
        Exception             : 
            Type        : System.Management.Automation.CommandNotFoundException
            ErrorRecord : 
                Exception             : 
                    Type    : System.Management.Automation.ParentContainsErrorRecordException
                    Message : The term 'Get-MgUser' is not recognized as a name of a cmdlet, function, script file, or executable program.
            Message     : The term 'Get-MgUser' is not recognized as a name of a cmdlet, function, script file, or executable program.
                          Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
            Data        : System.Collections.ListDictionaryInternal
            Source      : System.Management.Automation
            HResult     : -2146233087
        TargetObject          : Get-MgUser
        CategoryInfo          : ObjectNotFound: (Get-MgUser:String) [], CommandNotFoundException
        FullyQualifiedErrorId : CommandNotFoundExceptionC:\home\site\wwwroot\TimerTrigger1\run.ps1
            Line             : $userList = Get-MgUser -Filter "mail eq '$userEmail' or userPrincipalName eq '$userEmail'"  -ConsistencyLevel eventual -All -Property Id, DisplayName, UserPrincipalName, Mail
                               
            Statement        : Get-MgUser
            PositionMessage  : At 
        2025-05-08T22:03:07Z   [Error]   ERROR: The term 'Get-DistributionGroup' is not recognized as a name of a cmdlet, function, script file, or executable program.
                    HResult : -2146233087
                TargetObject          : Get-DistributionGroup
                CategoryInfo          : ObjectNotFound: (Get-DistributionGroup:String) [], ParentContainsErrorRecordException
                FullyQualifiedErrorId : CommandNotFoundException
    C:\home\site\wwwroot\TimerTrigger1\run.ps1
                    Line             : $dlGroups = Get-DistributionGroup -ResultSize Unlimited
                                       
                    Statement        : Get-DistributionGroup
                    PositionMessage  : At C:\home\site\wwwroot\TimerTrigger1\run.ps1:73 char:13
                                       + $dlGroups = Get-DistributionGroup -ResultSize Unlimited
                                       +             ~~~~~~~~~~~~~~~~~~~~~
                    PSScriptRoot     : C:\home\site\wwwroot\TimerTrigger1
                    PSCommandPath    : C:\home\site\wwwroot\TimerTrigger1\run.ps1
                    InvocationName   : Get-DistributionGroup
                    CommandOrigin    : Internal
                ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\TimerTrigger1\run.ps1: line 73
            CommandName : Get-DistributionGroup
            TargetSite  : 
                Name          : LookupCommandInfo
                DeclaringType : [System.Management.Automation.CommandDiscovery]
                MemberType    : Method
                Module        : System.Management.Automation.dll
            Message     : The term 'Get-DistributionGroup' is not recognized as a name of a cmdlet, function, script file, or executable program.
                          Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
            Data        : System.Collections.ListDictionaryInternal
            Source      : System.Management.Automation
            HResult     : -2146233087
        TargetObject          : Get-DistributionGroup
        CategoryInfo          : ObjectNotFound: (Get-DistributionGroup:String) [], CommandNotFoundException
        FullyQualifiedErrorId : CommandNotFoundException
    C:\home\site\wwwroot\TimerTrigger1\run.ps1
            Line             : $dlGroups = Get-DistributionGroup -ResultSize Unlimited
                               
            Statement        : Get-DistributionGroup
            PositionMessage  : At C:\home\site\wwwroot\TimerTrigger1\run.ps1:73 char:13
                               + $dlGroups = Get-DistributionGroup -ResultSize Unlimited

        2025-05-08T22:03:08Z   [Error]   ERROR: The specified module 'ExchangeOnlineManagement' was not loaded because no valid module file was found in any module directory.
        
        Exception             : 
            Type    : System.IO.FileNotFoundException
            Message : The specified module 'ExchangeOnlineManagement' was not loaded because no valid module file was found in any module directory.
            HResult : -2147024894
        TargetObject          : ExchangeOnlineManagement
        CategoryInfo          : ResourceUnavailable: (ExchangeOnlineManagement:String) [Import-Module], FileNotFoundException
        FullyQualifiedErrorId : Modules_ModuleNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand
        InvocationInfo        : 
        ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\TimerTrigger1\run.ps1: line 2
        PipelineIterationInfo : 
    
        2025-05-08T22:03:08Z   [Error]   ERROR: The specified module 'Microsoft.Graph' was not loaded because no valid module file was found in any module directory.
        
        Exception             : 
            Type    : System.IO.FileNotFoundException
            Message : The specified module 'Microsoft.Graph' was not loaded because no valid module file was found in any module directory.
            HResult : -2147024894
        TargetObject          : Microsoft.Graph
        CategoryInfo          : ResourceUnavailable: (Microsoft.Graph:String) [Import-Module], FileNotFoundException
        FullyQualifiedErrorId : Modules_ModuleNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand
        InvocationInfo        : 
        ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\TimerTrigger1\run.ps1: line 3
        PipelineIterationInfo : 
    
        2025-05-08T22:03:08Z   [Error]   ERROR: The term 'Connect-MgGraph' is not recognized as a name of a cmdlet, function, script file, or executable program.
                    HResult : -2146233087
                TargetObject          : Connect-MgGraph
                CategoryInfo          : ObjectNotFound: (Connect-MgGraph:String) [], ParentContainsErrorRecordException
                FullyQualifiedErrorId : CommandNotFoundException
                ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\TimerTrigger1\run.ps1: line 6
            CommandName : Connect-MgGraph
            TargetSite  : 
                Name          : LookupCommandInfo
                DeclaringType : [System.Management.Automation.CommandDiscovery]
                MemberType    : Method
                Module        : System.Management.Automation.dll
            Message     : The term 'Connect-MgGraph' is not recognized as a name of a cmdlet, function, script file, or executable program.

        
        2025-05-08T22:03:09Z   [Error]   ERROR: The term 'Connect-ExchangeOnline' is not recognized as a name of a cmdlet, function, script file, or executable program.
                    HResult : -2146233087
                TargetObject          : Connect-ExchangeOnline
                CategoryInfo          : ObjectNotFound: (Connect-ExchangeOnline:String) [], ParentContainsErrorRecordException
                FullyQualifiedErrorId : CommandNotFoundException
                ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\TimerTrigger1\run.ps1: line 9
            CommandName : Connect-ExchangeOnline
            TargetSite  : 
                Name          : LookupCommandInfo
                DeclaringType : [System.Management.Automation.CommandDiscovery]
                MemberType    : Method
                Module        : System.Management.Automation.dll
            Message     : The term 'Connect-ExchangeOnline' is not recognized as a name of a cmdlet, function, script file, or executable program.
                          Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
            Data        : System.Collections.ListDictionaryInternal
            Source      : System.Management.Automation
            StackTrace  : 
        TargetObject          : Connect-ExchangeOnline
        CategoryInfo          : ObjectNotFound: (Connect-ExchangeOnline:String) [], CommandNotFoundException
        FullyQualifiedErrorId : CommandNotFoundException
        InvocationInfo        : 
        ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\TimerTrigger1\run.ps1: line 9
        
        2025-05-08T22:03:09Z   [Error]   ERROR: The term 'Get-MgUser' is not recognized as a name of a cmdlet, function, script file, or executable program.
                    HResult : -2146233087
                    InvocationName   : Get-MgUser
                    CommandOrigin    : Internal
                ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\TimerTrigger1\run.ps1: line 15
            CommandName : Get-MgUser
            TargetSite  : 
                Name          : LookupCommandInfo
                DeclaringType : [System.Management.Automation.CommandDiscovery]
                MemberType    : Method
                Module        : System.Management.Automation.dll
            Message     : The term 'Get-MgUser' is not recognized as a name of a cmdlet, function, script file, or executable program.
        TargetObject          : Get-MgUser
        CategoryInfo          : ObjectNotFound: (Get-MgUser:String) [], CommandNotFoundException
        FullyQualifiedErrorId : CommandNotFoundException
        ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\TimerTrigger1\run.ps1: line 15
        
        2025-05-08T22:03:09Z   [Error]   ERROR: The term 'Get-DistributionGroup' is not recognized as a name of a cmdlet, function, script file, or executable program.
        Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    System.Management.Automation.CommandNotFoundExceptionSystem.Management.Automation.ParentContainsErrorRecordException
                    Message : The term 'Get-DistributionGroup' is not recognized as a name of a cmdlet, function, script file, or executable program.
                              Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
                    HResult : -2146233087
                TargetObject          : Get-DistributionGroup
                CategoryInfo          : ObjectNotFound: (Get-DistributionGroup:String) [], ParentContainsErrorRecordException
                FullyQualifiedErrorId : CommandNotFoundException
                InvocationInfo        : 
        
                ScriptStackTrace      : at <ScriptBlock>, C:\home\site\wwwroot\TimerTrigger1\run.ps1: line 73
            CommandName : Get-DistributionGroup
                Module        : System.Management.Automation.dll
            Message     : The term 'Get-DistributionGroup' is not recognized as a name of a cmdlet, function, script file, or executable program.
Azure Functions
Azure Functions
An Azure service that provides an event-driven serverless compute platform.
5,932 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Pravallika Kothaveeranna Gari 955 Reputation points Microsoft External Staff Moderator
    2025-05-20T18:03:20.6133333+00:00

    Hi john john Pter,

    The specified module 'ExchangeOnlineManagement' was not loaded because no valid module file was found in any module directory.

    The error shows that Azure Functions is not loading the required modules ExchangeOnlineManagement, Microsoft.Graph , despite adding them in requirements.psd1.

    Follow below steps to resolve the Module errors:

    1. Create a folder inside the function project and save the required modules in that folder and deploy the function to Azure.

    Open Terminal, navigate to the root directory of the project and run below commands:

    
    mkdir Modules //Creating Modules folder in the root directory of the function project
    
    cd Modules //cd to Modules folder to save the installed modules
    
    Save-Module -Name ExchangeOnlineManagement -Path Modules
    
    Save-Module -Name Microsoft.Graph -Path Modules
    
    

    This creates a folder named modules and saves the installed modules in that folder.

    image

    Add Modules folder in .gitignore.

    1. Remove Import-Module statements in run.ps1.
    2. Comment everything in Profile.ps1 or remove below lines from Profile.ps1 :
    
    Disable-AzContextAutosave -Scope Process | Out-Null
    
    Connect-AzAccount -Identity
    
    
    1. Connect-ExchangeOnline doesn't support -Identity parameter, use the below command instead:
    
    Connect-ExchangeOnline -ManagedIdentity -Organization <yourdomain>.onmicrosoft.com
    
    

    Use below modified code:

    
    param($Timer)
    
    # Connect to Microsoft Graph with required delegated scopes
    
     Write-Host "Start"
    
    Connect-MgGraph -Identity
    
     Write-Host "Connected to Graph"
    
    # Connect to Exchange Online
    
    Connect-ExchangeOnline -ManagedIdentity -Organization <yourdomain>.onmicrosoft.com
    
    Write-Host "Connected to Graph"
    
    # Target user email
    
    $userEmail = "usermail" 
    
    # --- PART 1: Azure AD + Office 365 Groups ---
    
    $userList = Get-MgUser -Filter "mail eq '$userEmail' or userPrincipalName eq '$userEmail'"  -ConsistencyLevel eventual -All -Property Id, DisplayName, UserPrincipalName, Mail
    
    $userObj = $userList | Select-Object -First 1
    
    if (-not $userObj) {
    
        Write-Host "User $userEmail not found in Microsoft Graph. Skipping AAD removal."
    
    } elseif (-not $userObj.Id) {
    
        Write-Host "User objectId missing even after lookup. Skipping AAD removal."
    
    } else {
    
        Write-Host "Found user: $($userObj.DisplayName) ($($userObj.UserPrincipalName))"
    
        $groups = Get-MgUserMemberOf -UserId $userObj.Id -All
    
        $groupsAsOwner = Get-MgUserOwnedObject -UserId $userObj.Id -All
    
        foreach ($group in $groups) {
    
            if ($group.AdditionalProperties.'@odata.type' -ne '#microsoft.graph.group') {
    
                continue
    
            }
    
            $groupId = $group.Id
    
            $groupName = $group.AdditionalProperties.DisplayName
    
            if (-not $groupId) {
    
                Write-Host "Skipping group with empty ID"
    
                continue
    
            }
    
            $fullGroup = Get-MgGroup -GroupId $groupId
    
            if ($fullGroup.GroupTypes -contains "DynamicMembership") {
    
                Write-Host "Skipping dynamic group: ${groupName}"
    
                continue
    
            }
    
            # Remove as MEMBER
    
            try {
    
                Remove-MgGroupMemberByRef -GroupId $groupId -DirectoryObjectId $userObj.Id -ErrorAction Stop
    
                Write-Host "Removed as MEMBER from ${groupName}"
    
            }
    
            catch {
    
                Write-Host "Failed MEMBER removal from ${groupName}: $($_.Exception.Message)"
    
            }}
    
    foreach ($group2 in $groupsAsOwner) {
    
    $groupId2 = $group2.Id
    
    $groupName2 = $group2.AdditionalProperties.DisplayName
    
            # Remove as OWNER
    
            try {
    
                Remove-MgGroupOwnerByRef -GroupId $groupId2 -DirectoryObjectId $userObj.Id -ErrorAction Stop
    
                Write-Host "Removed as OWNER from ${groupName2}"
    
            }
    
            catch {
    
                Write-Host "Failed OWNER removal from ${groupName2}: $($_.Exception.Message)"
    
            }
    
        }
    
        }
    
    # --- PART 2: Exchange Distribution Groups + Mail-enabled Security Groups ---
    
    $dlGroups = Get-DistributionGroup -ResultSize Unlimited
    
    foreach ($dl in $dlGroups) {
    
        try {
    
            # Remove as MEMBER
    
            $members = Get-DistributionGroupMember -Identity $dl.Identity -ResultSize Unlimited
    
            $memberMatch = $members | Where-Object { $_.PrimarySmtpAddress -ieq $userEmail }
    
            if ($memberMatch) {
    
                Remove-DistributionGroupMember -Identity $dl.Identity -Member $userEmail -BypassSecurityGroupManagerCheck -Confirm:$false
    
                Write-Host "Removed as MEMBER from Exchange group: $($dl.DisplayName)"
    
            }
    
            # Remove as OWNER
    
            # $owners = (Get-DistributionGroup -Identity $dl.Identity).ManagedBy
    
            $owners = (Get-DistributionGroup -Identity $dl.Identity).ManagedBy | Foreach-Object { Get-Mailbox $_ }
    
            $ownerMatch = $owners | Where-Object { $_.PrimarySmtpAddress -ieq $userEmail }
    
            if ($ownerMatch) {
    
                Set-DistributionGroup -Identity $dl.Identity -ManagedBy @{ Remove = "$userEmail" }
    
                Write-Host "Removed as OWNER from Exchange group: $($dl.DisplayName)"
    
            }
    
        }
    
        catch {
    
            Write-Host "Failed Exchange removal for group: $($dl.DisplayName) - $($_.Exception.Message)"
    
        }
    
    }
    
    Write-Host "Cleanup script completed."
    
    

    requirements.ps1:

    
    @{
    
        'Az'  =  '13.*'
    
        'ExchangeOnlineManagement'  =  '3.*'
    
        'Microsoft.Graph'  =  '2.*'
    
    }
    
    
    1. Deploy the function to Azure (Build the function before deploying):
    
    PS func azure functionapp publish kpsfn
    
    Getting site publishing info...
    
    [2025-05-20T12:00:14.582Z] Starting the function app deployment...
    
    Creating archive for current directory...
    
    Uploading 186.22 MB [#############################################################################]
    
    Creating archive for current directory...
    
    Uploading 186.22 MB [#############################################################################]
    
    Upload completed successfully.
    
    Deployment completed successfully.
    
    [2025-05-20T12:21:17.009Z] Syncing triggers...
    
    Functions in kpsfn:
    
        TimerTrigger - [timerTrigger]
    
    

    You should be able to see the Modules folder(contains the installed modules) in KUDU site https://functionappname.scm.azurewebsites.net/DebugConsole=>site/wwwroot.

    image

    image

    I have deployed the code to Azure function App with consumption plan (Powershell 7.4).

    • Please wait for a few minutes (min 5 minutes) while the function imports the required modules from PowerShell Gallery or the deployed Modules folder.

    Able to run the commands successfully:

    image

    If the function App still doesn't recognize the commands, check if you have the Modules folder under Site/wwwroot in Kudu site of your function app. If yes, import the module using the full path.

    Code Snippet:

    
    # Specify the full path to the module
    
    $modulePath = $env:HOME\site\wwwroot\<YourFunctionAppName>\modules\Microsoft.Graph\<ModuleVersion>
    
    # Import the module
    
    Import-Module $modulePath
    
    # Use Connect-MgGraph
    
    Connect-MgGraph
    
    

    To connect to Exchange Online, you need to assign Administrative Role to the Managed Identity and grant Admin Consent to the Service Principal.

    Hope it helps!


    Please do not forget to click "Accept the answer” and Yes wherever the information provided helps you, this can be beneficial to other community members.

    User's image

    If you have any other questions or still running into more issues, let me know in the "comments" and I would be happy to help you.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.