Windows용 Log Analytics 에이전트 관련 문제 해결

이 문서는 Azure Monitor의 Windows용 Log Analytics 에이전트에서 발생할 수 있는 오류 문제를 해결하는 데 도움을 주고 이를 해결할 수 있는 솔루션을 제안합니다.

Log Analytics 문제 해결 도구

Windows용 Log Analytics 에이전트 문제 해결 도구는 Log Analytics 에이전트와 관련된 문제를 찾고 진단하는 데 도움이 되도록 설계된 PowerShell 스크립트 모음입니다. 이는 설치 시 자동으로 에이전트에 포함됩니다. 이 도구의 실행은 문제를 진단하는 첫 번째 단계여야 합니다.

문제 해결 도구 사용

  1. Log Analytics 에이전트가 설치된 컴퓨터에서 관리자 권한으로 PowerShell 프롬프트를 엽니다.

  2. 도구가 있는 디렉터리로 이동합니다.

    cd "C:\Program Files\Microsoft Monitoring Agent\Agent\Troubleshooter"

  3. 다음 명령을 사용하여 주 스크립트를 실행합니다.

    .\GetAgentInfo.ps1

  4. 문제 해결 시나리오를 선택합니다.

  5. 콘솔의 지침을 따릅니다. 추적 로그 단계에서는 로그 수집을 중지하기 위해 수동 개입이 필요합니다. 문제의 재현 가능성에 따라 해당 기간 동안 기다리고 "s"를 선택하여 로그 수집을 중지하고 다음 단계로 계속 진행합니다.

    결과 파일의 위치는 완료 시 로그되고 이를 강조 표시하는 새 탐색기 창이 열립니다.

설치

문제 해결 도구는 Log Analytics 에이전트 빌드 10.20.18053.0 이상을 설치할 때 자동으로 포함됩니다.

포함되는 시나리오

문제 해결 도구에서 확인하는 시나리오는 다음과 같습니다.

  • 에이전트에서 데이터를 보고하지 않거나 하트비트 데이터가 누락되었습니다.
  • 에이전트 확장 배포 오류가 발생합니다.
  • 에이전트가 충돌합니다.
  • 에이전트에서 높은 CPU 또는 메모리를 사용하고 있습니다.
  • 설치 및 제거 환경 오류입니다.
  • 사용자 지정 로그에 문제가 있습니다.
  • OMS 게이트웨이에 문제가 있습니다.
  • 성능 카운터에 문제가 있습니다.
  • 에이전트 로그를 수집할 수 없습니다.

참고 항목

문제가 발생하면 문제 해결 도구를 실행합니다. 처음에 로그가 있으면 지원 팀에서 문제를 더 빨리 해결하는 데 도움이 됩니다.

중요한 문제 해결 원본

Windows용 Log Analytics 에이전트와 관련된 문제 해결을 지원하기 위해 에이전트는 특히 Application and Services\Operations Manager 아래의 Windows 이벤트 로그에 이벤트를 기록합니다.

연결 문제

에이전트에서 프록시 서버 또는 방화벽을 통해 통신하는 경우 원본 컴퓨터와 Azure Monitor 서비스의 통신을 차단하는 제한 사항이 있을 수 있습니다. 잘못된 구성으로 인해 통신이 차단되는 경우 에이전트를 설치하거나 다른 작업 영역에 보고하도록 에이전트 사후 설정을 구성하려고 하는 동안 작업 영역에 대한 등록이 실패할 수 있습니다. 성공적으로 등록되면 에이전트 통신이 실패할 수 있습니다. 이 섹션에서는 이러한 유형의 문제를 해결하는 방법을 설명합니다.

다음 표에서 설명하는 포트 및 URL을 허용하도록 방화벽 또는 프록시가 구성되어 있는지 다시 확인합니다. 또한 HTTP 검사가 웹 트래픽에 사용하도록 설정되어 있지 않은지 확인합니다. 에이전트와 Azure Monitor 간의 보안 TLS 채널을 차단할 수 있습니다.

에이전트 리소스 포트 Direction HTTP 검사 바이패스
*.ods.opinsights.azure.com 포트 443 아웃바운드
*.oms.opinsights.azure.com 포트 443 아웃바운드
*.blob.core.windows.net 포트 443 아웃바운드
*.agentsvc.azure-automation.net 포트 443 아웃바운드

Azure Government에 필요한 방화벽 정보는 Azure Government 관리를 참조하세요. Azure Automation Hybrid Runbook Worker를 사용하여 사용자 환경에서 Runbook이나 관리 솔루션을 사용하기 위해 Automation 서비스에 연결하고 등록하려면 Hybrid Runbook Worker에 대한 네트워크 구성에 설명된 URL 및 포트 번호에 대한 액세스 권한이 있어야 합니다.

에이전트에서 Azure Monitor와 성공적으로 통신하고 있는지 확인할 수 있는 몇 가지 방법이 있습니다.

  • 작업 영역에서 Azure Log Analytics 에이전트 상태 평가를 사용하도록 설정합니다. 에이전트 상태 대시보드에서 응답하지 않는 에이전트 수 열을 확인하여 에이전트가 나열되어 있는지 빠르게 확인합니다.

  • 다음 쿼리를 실행하여 에이전트에서 보고하도록 구성된 작업 영역에 하트비트를 보내고 있는지 확인합니다. <ComputerName>을 머신의 실제 이름으로 바꿉니다.

    Heartbeat 
    | where Computer like "<ComputerName>"
    | summarize arg_max(TimeGenerated, * ) by Computer 
    

    컴퓨터가 서비스와 성공적으로 통신하는 경우 쿼리는 결과를 반환해야 합니다. 쿼리에서 결과를 반환하지 않은 경우 먼저 에이전트가 올바른 작업 영역에 보고하도록 구성되어 있는지 확인합니다. 올바르게 구성된 경우 3단계로 계속 진행하고 Windows 이벤트 로그를 검색하여 에이전트에서 Azure Monitor와의 통신을 차단할 수 있는 문제를 로그하고 있는지 확인합니다.

  • 연결 문제를 확인하는 또 다른 방법은 TestCloudConnectivity 도구를 실행하는 것입니다. 이 도구는 기본적으로 %SystemRoot%\Program Files\Microsoft Monitoring Agent\Agent 폴더에 에이전트와 함께 설치됩니다. 관리자 권한 명령 프롬프트에서 폴더로 이동하여 도구를 실행합니다. 이 도구는 결과를 반환하고 테스트에 실패한 위치를 강조 표시합니다. 예를 들어 차단된 특정 포트 또는 URL과 관련되었을 수 있습니다.

    Screenshot that shows TestCloudConnection tool execution results.

  • Operations Manager 이벤트 로그를 이벤트 원본상태 관리 서비스 모듈, HealthService서비스 커넥터를 기준으로 필터링하고 이벤트 수준경고오류를 기준으로 필터링하여 다음 표의 이벤트를 기록했는지 확인합니다. 이벤트가 기록된 경우 가능한 각 이벤트에 대해 포함된 해결 단계를 검토합니다.

    이벤트 ID 원본 설명 해결 방법
    2133 및 2129 상태 관리 서비스 에이전트에서 서비스에 연결하지 못했습니다. 에이전트에서 Azure Monitor 서비스와 직접 통신하거나 방화벽 또는 프록시 서버를 통해 통신할 수 없는 경우 이 오류가 발생할 수 있습니다. 에이전트 프록시 설정을 확인하거나 네트워크 방화벽 또는 프록시가 컴퓨터에서 서비스로의 TCP 트래픽을 허용하는지 확인합니다.
    2138 상태 관리 서비스 모듈 프록시에 인증이 필요합니다. 에이전트 프록시 설정을 구성하고 프록시 서버에서 인증을 받는 데 필요한 사용자 이름/암호를 지정합니다.
    2129 상태 관리 서비스 모듈 연결에 실패했습니다. TLS 협상에 실패했습니다. 네트워크 어댑터 TCP/IP 설정 및 에이전트 프록시 설정을 확인합니다.
    2127 상태 관리 서비스 모듈 데이터 전송 실패 오류 코드를 받았습니다. 하루 동안 주기적으로 발생하는 경우에만 무시할 수 있는 임의 변칙일 수 있습니다. 모니터링하여 발생 빈도를 파악합니다. 하루 종일 자주 발생하는 경우 먼저 네트워크 구성 및 프록시 설정을 확인합니다. 설명에 HTTP 오류 코드 404가 포함되어 있고 에이전트가 처음으로 서비스에 데이터를 보내려고 할 때 내부 404 오류 코드와 함께 500 오류가 포함됩니다. 404 오류 코드는 "찾을 수 없음"을 의미하며, 새 작업 영역에 대한 스토리지 영역이 아직 프로비전되고 있음을 나타냅니다. 다음에 다시 시도하면 데이터가 예상대로 작업 영역에 성공적으로 기록됩니다. HTTP 오류 403은 사용 권한 또는 자격 증명 문제를 나타낼 수 있습니다. 문제를 해결하는 데 도움이 되는 자세한 정보는 403 오류에 포함되어 있습니다.
    4000 서비스 커넥터 DNS 이름 확인에 실패했습니다. 컴퓨터에서 데이터를 서비스에 보낼 때 사용된 인터넷 주소를 확인할 수 없습니다. 이 문제는 컴퓨터의 DNS 확인자 설정, 잘못된 프록시 설정 또는 공급자와 관련된 일시적인 DNS 문제일 수 있습니다. 정기적으로 발생하는 경우 일시적인 네트워크 관련 문제로 인해 발생할 수 있습니다.
    4001 서비스 커넥터 서비스에 연결하지 못했습니다. 에이전트에서 Azure Monitor 서비스와 직접 통신하거나 방화벽 또는 프록시 서버를 통해 통신할 수 없는 경우 이 오류가 발생할 수 있습니다. 에이전트 프록시 설정을 확인하거나 네트워크 방화벽 또는 프록시가 컴퓨터에서 서비스로의 TCP 트래픽을 허용하는지 확인합니다.
    4002 서비스 커넥터 서비스는 쿼리에 대한 응답으로 HTTP 상태 코드 403을 반환했습니다. 서비스 관리자에게 서비스의 상태를 확인합니다. 쿼리는 나중에 다시 시도됩니다. 이 오류는 에이전트의 초기 등록 단계에서 기록됩니다. https://<workspaceID>.oms.opinsights.azure.com/AgentService.svc/AgentTopologyRequest와 비슷한 URL이 표시됩니다. 403 오류 코드는 "사용할 수 없음"을 의미하며, 잘못 입력된 작업 영역 ID 또는 키로 인해 발생할 수 있습니다. 컴퓨터에서 날짜와 시간이 올바르지 않을 수도 있습니다. 시간이 현재 시간에서 15분 이상/이하인 경우 온보딩이 실패합니다. 이 문제를 해결하려면 Windows 컴퓨터의 날짜 및/또는 시간을 업데이트합니다.

데이터 수집 문제

에이전트가 설치되고 구성된 작업 영역에 보고한 후 컴퓨터를 사용하도록 설정하고 대상으로 지정하는 항목에 따라 구성을 수신하고 성능, 로그 또는 기타 데이터를 서비스에 수집하거나 전달하는 작업을 중지할 수 있습니다. 다음을 결정해야 합니다.

  • 특정 데이터 형식인가요, 아니면 작업 영역에서 사용할 수 없는 모든 데이터인가요?
  • 솔루션에 지정된 데이터 형식 또는 작업 영역 데이터 수집 구성의 일부로 지정된 데이터 형식이 있는지 확인합니다.
  • 영향을 받은 컴퓨터가 몇 대인지 확인합니다. 단일 컴퓨터인가, 아니면 작업 영역에 보고하는 여러 컴퓨터인가요?
  • 아직 작업 중인지, 특정 시간에 작업을 중지했는지 또는 수집이 전혀 수행되지 않았는지 확인합니다.
  • 사용하고 있는 로그 검색 쿼리가 구문상 올바른가요?
  • 에이전트가 Azure Monitor에서 구성을 받았는지 확인합니다.

문제 해결의 첫 번째 단계는 컴퓨터가 하트비트 이벤트를 전송하고 있는지 확인하는 것입니다.

Heartbeat 
    | where Computer like "<ComputerName>"
    | summarize arg_max(TimeGenerated, * ) by Computer

쿼리에서 결과를 반환하는 경우 특정 데이터 형식이 수집되어 서비스에 전달되지 않는지 확인해야 합니다. 이 문제는 에이전트에서 업데이트된 구성을 서비스로부터 받지 못하거나 에이전트가 정상적으로 작동하지 못하게 하는 다른 증상으로 인해 발생할 수 있습니다. 추가로 문제를 해결하려면 다음 단계를 수행합니다.

  1. 컴퓨터에서 관리자 권한 명령 프롬프트를 열고, net stop healthservice && net start healthservice를 입력하여 에이전트 서비스를 다시 시작합니다.

  2. Operations Manager 이벤트 로그를 열고, 이벤트 원본HealthService에서 이벤트 ID7023, 7024, 7025, 70281210을 검색합니다. 이러한 이벤트는 에이전트에서 구성을 Azure Monitor로부터 성공적으로 받고 있으며 컴퓨터를 적극적으로 모니터링하고 있음을 나타냅니다. 이벤트 ID 1210에 대한 이벤트 설명도 에이전트의 모니터링 범위에 포함된 모든 솔루션과 인사이트를 마지막 줄에 지정합니다.

    Screenshot that shows an Event ID 1210 description.

  3. 몇 분 정도 기다립니다. 예상 데이터가 쿼리 결과 또는 시각화에 표시되지 않으면 솔루션 또는 인사이트에서 데이터를 보고 있는지 여부에 따라 Operations Manager 이벤트 로그에서 이벤트 원본HealthService상태 관리 서비스 모듈을 검색합니다. 이벤트 수준경고오류를 기준으로 필터링하여 다음 표의 이벤트를 기록했는지 확인합니다.

    이벤트 ID 원본 설명 해결 방법
    8000 HealthService 이 이벤트는 성능, 이벤트 또는 수집된 다른 데이터 형식과 관련된 워크플로가 작업 영역에 수집하기 위해 서비스로 전달할 수 없는지를 지정합니다. HealthService 원본의 2136 이벤트 ID는 이 이벤트와 함께 기록되며, 에이전트에서 서비스와 통신할 수 없음을 나타낼 수 있습니다. 가능한 이유는 프록시 및 인증 설정의 잘못된 구성, 네트워크 중단 또는 컴퓨터에서 서비스로의 TCP 트래픽을 허용하지 않는 네트워크 방화벽 또는 프록시일 수 있습니다.
    10102 및 10103 상태 관리 서비스 모듈 워크플로에서 데이터 원본을 확인할 수 없습니다. 지정된 성능 카운터 또는 인스턴스가 컴퓨터에 없거나 작업 영역 데이터 설정에서 잘못 정의된 경우 이 문제가 발생할 수 있습니다. 사용자 지정 성능 카운터인 경우 지정된 정보가 올바른 형식을 따르고 대상 컴퓨터에 있는지 확인합니다.
    26002 상태 관리 서비스 모듈 워크플로에서 데이터 원본을 확인할 수 없습니다. 지정된 Windows 이벤트 로그가 컴퓨터에 없는 경우 이 문제가 발생할 수 있습니다. 이 이벤트 로그가 컴퓨터에 등록되지 않을 것으로 예상되는 경우 이 오류는 무시해도 됩니다. 그렇지 않고 사용자 지정 이벤트 로그인 경우 지정된 정보가 올바른지 확인합니다.

이전 Microsoft Monitoring Agents의 고정된 인증서 문제 - 호환성이 손상되는 변경

루트 CA 변경 개요

2023년 6월 30일부터 Log Analytics 백 엔드는 더 이상 오래된 루트 인증서를 참조하는 MMA의 연결을 수락하지 않습니다. 이러한 MMA는 2020년 겨울 릴리스(Log Analytics 에이전트) 이전 및 SCOM 2019 UR3(SCOM) 이전 버전입니다. 모든 버전, 번들: 10.20.18053/확장: 1.0.18053.0 이상에는 문제가 없으며 SCOM 2019 UR3 이상 버전에서도 문제가 나타나지 않습니다. 이보다 오래된 에이전트는 중단되고 더 이상 작동하지 않고 Log Analytics에 업로드됩니다.

변경되는 사항은 정확히 무엇인가요?

다양한 Azure 서비스에서 지속적인 보안 노력의 일환으로 Azure Log Analytics는 공식적으로 Baltimore CyberTrust CA 루트에서 DigiCert Global G2 CA 루트로 전환됩니다. 이 변경 내용은 새 DigiCert Global G2 CA 루트 인증서가 OS에서 누락되었거나 애플리케이션이 이전 Baltimore 루트 CA를 참조하는 경우 Log Analytics와의 TLS 통신에 영향을 줍니다. 이것은 Log Analytics가 사용 중지된 후 이 이전 루트 CA를 사용하는 MMA의 연결을 더 이상 허용하지 않다는 것을 의미합니다.

솔루션 제품

Microsoft Monitoring Agent를 개인적으로 설치하지 않은 경우에도 호환성이 손상되는 변경 알림을 받았을 수 있습니다. 다양한 Azure 제품이 Microsoft Monitoring Agent를 활용하기 때문입니다. 이러한 제품 중 하나를 사용하는 경우 Windows Log Analytics 에이전트를 활용하므로 영향을 받을 수 있습니다. 아래 링크가 있는 제품의 경우 최신 에이전트로 업그레이드해야 하는 특정 지침이 있을 수 있습니다.

호환성이 손상되는 에이전트 식별 및 수정

에이전트 수가 제한된 배포의 경우 이러한 관리 지침을 통해 노드당 에이전트를 업그레이드하는 것이 좋습니다.

여러 노드가 있는 배포의 경우 영향을 받는 구독당 호환성이 손상되는 모든 MMA를 검색한 다음, 최신 버전으로 업그레이드하는 스크립트를 작성했습니다. 이러한 스크립트는 UpdateMMA.ps1부터 시작한 후 UpgradeMMA.ps1까지 순차적으로 실행해야 합니다. 컴퓨터에 따라 스크립트에 다소 시간이 걸릴 수 있습니다. 시간 제한을 방지하기 위해 PowerShell 7 이상을 실행해야 합니다.

UpdateMMA.ps1 이 스크립트는 구독의 VM을 거치면서 설치된 기존 MMA를 검사한 후 업그레이드해야 하는 에이전트의 .csv 파일을 생성합니다.

UpgradeMMA.ps1 이 스크립트는 UpdateMMA.ps1 생성된 .CSV 파일을 사용하여 호환성이 손상되는 모든 MMA를 업그레이드합니다.

이 두 스크립트를 모두 완료하는 데 다소 시간이 걸릴 수 있습니다.

# UpdateMMA.ps1
# This script is to be run per subscription, the customer has to set the az subscription before running this within the terminal scope.
# This script uses parallel processing, modify the $parallelThrottleLimit parameter to either increase or decrease the number of parallel processes
# PS> .\UpdateMMA.ps1 GetInventory
# The above command will generate a csv file with the details of VM's and VMSS that require MMA upgrade. 
# The customer can modify the csv by adding/removing rows if needed
# Update the MMA by running the script again and passing the csv file as parameter as shown below:
# PS> .\UpdateMMA.ps1 Upgrade
# If you don't want to check the inventory, then run the script wiht an additional -no-inventory-check
# PS> .\UpdateMMA.ps1 GetInventory & .\UpdateMMA.ps1 Upgrade


# This version of the script requires Powershell version >= 7 in order to improve performance via ForEach-Object -Parallel
# https://docs.microsoft.com/powershell/scripting/whats-new/migrating-from-windows-powershell-51-to-powershell-7?view=powershell-7.1
if ($PSVersionTable.PSVersion.Major -lt 7) 
{
    Write-Host "This script requires Powershell version 7 or newer to run. Please see https://docs.microsoft.com/powershell/scripting/whats-new/migrating-from-windows-powershell-51-to-powershell-7?view=powershell-7.1."
    exit 1
}

$parallelThrottleLimit = 16
$mmaFixVersion = [version]"10.20.18053.0"

function GetVmsWithMMAInstalled
{
    param(
        $fileName
    )

    $vmList = az vm list --show-details --query "[?powerState=='VM running'].{ResourceGroup:resourceGroup, VmName:name}" | ConvertFrom-Json
    
    if(!$vmList)
    {
        Write-Host "Cannot get the VM list, this script can only detect the running VM's"
        return
    }

    $vmsCount = $vmList.Length
    
    $vmParallelThrottleLimit = $parallelThrottleLimit
    if ($vmsCount -lt $vmParallelThrottleLimit) 
    {
        $vmParallelThrottleLimit = $vmsCount
    }

    if($vmsCount -eq 1)
    {
        $vmGroups += ,($vmList[0])
    }
    else
    {
        # split the vm's into batches to do parallel processing
        for ($i = 0; $i -lt $vmsCount; $i += $vmParallelThrottleLimit) 
        { 
            $vmGroups += , ($vmList[$i..($i + $vmParallelThrottleLimit - 1)]) 
        }
    }

    Write-Host "Detected $vmsCount Vm's running in this subscription."
    $hash = [hashtable]::Synchronized(@{})
    $hash.One = 1

    $vmGroups | Foreach-Object -ThrottleLimit $parallelThrottleLimit -Parallel {
        $len = $using:vmsCount
        $hash = $using:hash
        $_ | ForEach-Object {
            $percent = 100 * $hash.One++ / $len
            Write-Progress -Activity "Getting VM Inventory" -PercentComplete $percent
            $vmName = $_.VmName
            $resourceGroup = $_.ResourceGroup
            $responseJson = az vm run-command invoke --command-id RunPowerShellScript --name $vmName -g $resourceGroup --scripts '@UpgradeMMA.ps1' --parameters "functionName=GetMMAVersion" --output json | ConvertFrom-Json
            if($responseJson)
            {
                $mmaVersion = $responseJson.Value[0].message
                if ($mmaVersion) 
                {
                    $extensionName = az vm extension list -g $resourceGroup --vm-name $vmName --query "[?name == 'MicrosoftMonitoringAgent'].name" | ConvertFrom-Json
                    if ($extensionName) 
                    {
                        $installType = "Extension"
                    }
                    else 
                    {
                        $installType = "Installer"
                    }
                    $csvObj = New-Object -TypeName PSObject -Property @{
                        'Name'           = $vmName
                        'Resource_Group' = $resourceGroup
                        'Resource_Type'  = "VM"
                        'Install_Type'   = $installType
                        'Version'        = $mmaVersion
                        "Instance_Id"    = ""
                    }
                    $csvObj | Export-Csv $using:fileName -Append -Force
                } 
            } 
        }
    }
}

function GetVmssWithMMAInstalled
{
    param(
        $fileName
    )

    # get the vmss list which are successfully provisioned
    $vmssList = az vmss list --query "[?provisioningState=='Succeeded'].{ResourceGroup:resourceGroup, VmssName:name}" | ConvertFrom-Json   

    $vmssCount = $vmssList.Length
    Write-Host "Detected $vmssCount Vmss running in this subscription."
    $hash = [hashtable]::Synchronized(@{})
    $hash.One = 1

    $vmssList | Foreach-Object -ThrottleLimit $parallelThrottleLimit -Parallel {
        $len = $using:vmssCount
        $hash = $using:hash
        $percent = 100 * $hash.One++ / $len
        Write-Progress -Activity "Getting VMSS Inventory" -PercentComplete $percent
        $vmssName = $_.VmssName
        $resourceGroup = $_.ResourceGroup

        # get running vmss instance ids
        $vmssInstanceIds = az vmss list-instances --resource-group $resourceGroup --name $vmssName --expand instanceView --query "[?instanceView.statuses[1].displayStatus=='VM running'].instanceId" | ConvertFrom-Json
        if ($vmssInstanceIds.Length -gt 0) 
        {
            $isMMAExtensionInstalled = az vmss extension list -g $resourceGroup --vmss-name $vmssName --query "[?name == 'MicrosoftMonitoringAgent'].name" | ConvertFrom-Json
            if ($isMMAExtensionInstalled ) 
            {
                # check an instance in vmss, if it needs an MMA upgrade. Since the extension is installed at VMSS level, checking for bad version in 1 instance should be fine.
                $responseJson = az vmss run-command invoke --command-id RunPowerShellScript --name $vmssName -g $resourceGroup --instance-id $vmssInstanceIds[0] --scripts '@UpgradeMMA.ps1' --parameters "functionName=GetMMAVersion" --output json | ConvertFrom-Json
                $mmaVersion = $responseJson.Value[0].message
                if ($mmaVersion) 
                {
                    $csvObj = New-Object -TypeName PSObject -Property @{
                        'Name'           = $vmssName
                        'Resource_Group' = $resourceGroup
                        'Resource_Type'  = "VMSS"
                        'Install_Type'   = "Extension"
                        'Version'        = $mmaVersion
                        "Instance_Id"    = ""
                    }
                    $csvObj | Export-Csv $using:fileName -Append -Force
                }
            }
            else 
            {
                foreach ($instanceId in $vmssInstanceIds) 
                {
                    $responseJson = az vmss run-command invoke --command-id RunPowerShellScript --name $vmssName -g $resourceGroup --instance-id $instanceId --scripts '@UpgradeMMA.ps1' --parameters "functionName=GetMMAVersion" --output json | ConvertFrom-Json
                    $mmaVersion = $responseJson.Value[0].message
                    if ($mmaVersion) 
                    {
                        $csvObj = New-Object -TypeName PSObject -Property @{
                            'Name'           = $vmssName
                            'Resource_Group' = $resourceGroup
                            'Resource_Type'  = "VMSS"
                            'Install_Type'   = "Installer"
                            'Version'        = $mmaVersion
                            "Instance_Id"    = $instanceId
                        }
                        $csvObj | Export-Csv $using:fileName -Append -Force
                    }
                }
            }
        }      
    }
}

function Upgrade
{
    param(
        $fileName = "MMAInventory.csv"
    )
    Import-Csv $fileName | ForEach-Object -ThrottleLimit $parallelThrottleLimit -Parallel {
        $mmaVersion = [version]$_.Version
        if($mmaVersion -lt $using:mmaFixVersion)
        {
            if ($_.Install_Type -eq "Extension") 
            {
                if ($_.Resource_Type -eq "VMSS") 
                {
                    # if the extension is installed with a custom name, provide the name using the flag: --extension-instance-name <extension name>
                    az vmss extension set --name MicrosoftMonitoringAgent --publisher Microsoft.EnterpriseCloud.Monitoring --force-update --vmss-name $_.Name --resource-group $_.Resource_Group --no-wait --output none
                }
                else 
                {
                    # if the extension is installed with a custom name, provide the name using the flag: --extension-instance-name <extension name>
                    az vm extension set --name MicrosoftMonitoringAgent --publisher Microsoft.EnterpriseCloud.Monitoring --force-update --vm-name $_.Name --resource-group $_.Resource_Group --no-wait --output none
                }
            }
            else {
                if ($_.Resource_Type -eq "VMSS") 
                {
                    az vmss run-command invoke --command-id RunPowerShellScript --name $_.Name -g $_.Resource_Group --instance-id $_.Instance_Id --scripts '@UpgradeMMA.ps1' --parameters "functionName=UpgradeMMA" --output none
                }
                else 
                {
                    az vm run-command invoke --command-id RunPowerShellScript --name $_.Name -g $_.Resource_Group --scripts '@UpgradeMMA.ps1' --parameters "functionName=UpgradeMMA" --output none
                }
            }
        }
    }
}

function GetInventory
{
    param(
        $fileName = "MMAInventory.csv"
    )

    # create a new file 
    New-Item -Name $fileName -ItemType File -Force
    GetVmsWithMMAInstalled $fileName
    GetVmssWithMMAInstalled $fileName
}

switch ($args.Count)
{
    0 {
        Write-Host "The arguments provided are incorrect."
        Write-Host "To get the Inventory: Run the script as: PS> .\UpdateMMA.ps1 GetInventory"
        Write-Host "To update MMA from Inventory: Run the script as: PS> .\UpdateMMA.ps1 Upgrade"
        Write-Host "To do the both steps together: PS> .\UpdateMMA.ps1 GetInventory & .\UpdateMMA.ps1 Upgrade"
    }
    1 {
        $funcname = $args[0]
        Invoke-Expression "& $funcname"
    }
    2 {
        $funcname = $args[0]
        $funcargs = $args[1]
        Invoke-Expression "& $funcname $funcargs"
    }
}