Windows 用 Log Analytics エージェントに関するイシューのトラブルシューティング
この記事では、Azure Monitor の Windows 用 Log Analytics エージェントで発生する可能性のあるエラーのトラブルシューティングのヘルプを提供し、それらを解決するための考えられる解決策を提案します。
Log Analytics のトラブルシューティング ツール
Windows 用 Log Analytics エージェントのトラブルシューティング ツールは、Log Analytics エージェントでのイシューの検出と診断に役立つように設計された、PowerShell スクリプトのコレクションです。 それは、インストール時にエージェントに自動的に含まれます。 このツールを実行することが、問題を診断するための最初の手順になります。
トラブルシューティング ツールを使用する
Log Analytics エージェントがインストールされているコンピューターで、管理者として PowerShell プロンプトを開きます。
ツールが置かれているディレクトリに移動します。
cd "C:\Program Files\Microsoft Monitoring Agent\Agent\Troubleshooter"
次のコマンドを使用してメイン スクリプトを実行します。
.\GetAgentInfo.ps1
トラブルシューティングのシナリオを選択します。
コンソールに表示される指示に従います。 トレース ログのステップでは、ログ収集を停止する際に手動による操作が必要です。 イシューの再現度に基づいて一定期間待ち、"S" を選択してログ収集を停止し、次のステップに進みます。
結果ファイルの場所は完了時にログされ、新しいエクスプローラー ウィンドウが、ファイルを強調表示した状態で表示されます。
インストール
トラブルシューティング ツールは、Log Analytics エージェントのビルド 10.20.18053.0 以降のインストール時に自動的に同梱されます。
対象となるシナリオ
トラブルシューティング ツールは、次のシナリオを確認します。
- エージェントがデータを報告していないか、ハートビート データがない。
- エージェントの拡張機能のデプロイが失敗している。
- エージェントがクラッシュしている。
- エージェントの CPU またはメモリの消費が高い。
- インストールとアンインストールのエクスペリエンスに問題がある。
- カスタム ログに問題がある。
- OMS ゲートウェイに問題がある。
- パフォーマンス カウンターに問題がある。
- エージェント ログを収集できない。
Note
イシューが発生した場合は、トラブルシューティング ツールを実行してください。 ログを最初に用意すれば、サポート チームが問題を迅速にトラブルシューティングするのに役立ちます。
重要なトラブルシューティング ソース
Windows 用 Log Analytics エージェントに関連するイシューのトラブルシューティングを支援するために、エージェントではイベントを Windows イベント ログ (特に アプリケーションとサービス\Operations Manager 以下) にログします。
接続に関する問題
エージェントがプロキシ サーバーまたはファイアウォールを介して通信する場合は、ソース コンピューターと Azure Monitor サービスからの通信を妨げる制限が設定されている場合があります。 不適切な構成のために通信がブロックされた場合、エージェントをインストールするときや、設定後に別のワークスペースに報告するようにエージェントを構成するときに、ワークスペースへの登録が失敗することがあります。 登録が成功した後で、エージェントの通信が失敗する可能性があります。 このセクションでは、Windows エージェントに関するこの種の問題を解決する方法について説明します。
次の表に示す以下のポートと URL を許可するようにファイアウォールまたはプロキシが構成されていることを再確認してください。 Web トラフィックに対して HTTP 検査が有効になっていないことも確認してください。 これによってエージェントと Azure Monitor の間で安全な TLS チャネルが妨げられる場合があります。
エージェントのリソース | Port | Direction | HTTPS検査をバイパス |
---|---|---|---|
*.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 を使用して Automation サービスに接続および登録し、お使いの環境で Runbook または管理ソリューションを使用することを計画している場合、Hybrid Runbook Worker 用のネットワークの構成に関する記事に説明されているポート番号と URL にアクセスできる必要があります。
エージェントが Azure Monitor と正常に通信しているかどうかを確認する方法はいくつかあります。
ワークスペースで [Azure Log Analytics Agent Health assessment](Azure Log Analytics の Agent Health の評価)を有効にします。 Agent Health ダッシュボードで [Count of unresponsive agents](応答していないエージェントの数) 列を表示すると、エージェントが一覧にあるかどうかを簡単に確認できます。
次のクエリを実行して、報告先に構成されているワークスペースにエージェントからハートビートが送信されていることを確認します。
<ComputerName>
は、マシンの実際の名前に置き換えます。Heartbeat | where Computer like "<ComputerName>" | summarize arg_max(TimeGenerated, * ) by Computer
コンピューターがサービスと正常に通信している場合、クエリからは結果が返されます。 クエリから結果が返されない場合は、まず適切なワークスペースに報告するようにエージェントが構成されていることを確認します。 正しく構成されている場合は、ステップ 3 に進み、Windows イベント ログを検索して、Azure Monitor との通信を妨げている可能性があるイシューがエージェントによってログされているかどうかを確認します。
接続の問題を特定するもう 1 つの方法は、TestCloudConnectivity ツールを実行することです。 このツールは、既定でエージェントと共にフォルダー %SystemRoot%\Program Files\Microsoft Monitoring Agent\Agent にインストールされます。 管理者特権でのコマンド プロンプトから、このフォルダーに移動してツールを実行します。 このツールにより結果が返され、どこでテストが失敗したかが強調表示されます。 たとえば、ブロックされた特定のポートまたは URL に関連していた可能性があります。
イベント ソース Health Service Modules、HealthService、Service Connector で Operations Manager イベント ログを絞り込み、イベント レベルの警告とエラーで絞り込んで、次の表のイベントが書き込まれたかどうかを確認します。 その場合は、発生する可能性がある各イベントについて記載されている解決手順を確認します。
イベント ID source 説明 解像度 2133 と 2129 Health Service エージェントからサービスへの接続に失敗しました。 このエラーは、エージェントが Azure Monitor サービスと直接またはファイアウォールやプロキシ サーバーを介して通信できない場合に発生する可能性があります。 エージェントのプロキシ設定、またはネットワーク ファイアウォールやプロキシでコンピューターからサービスへの TCP トラフィックが許可されていることを確認します。 2138 Health Service Modules プロキシに認証が必要です。 エージェント プロキシ設定を構成し、プロキシ サーバーとの認証に必要なユーザー名/パスワードを指定します。 2129 Health Service Modules 接続が失敗しました。 TLS ネゴシエーションに失敗しました。 ネットワーク アダプターの TCP/IP 設定とエージェント プロキシ設定を確認します。 2127 Health Service Modules データの送信に失敗し、エラー コードを受信しました。 1 日にときおり発生するだけの場合は、ランダムな異常として無視できる場合があります。 監視して発生する頻度を把握します。 1 日中頻繁に発生する場合は、まずネットワーク構成とプロキシ設定を確認します。 説明に HTTP エラー コード 404 が含まれていて、エージェントからサービスにデータを送信しようとしたのが初めての場合、内部 404 エラー コードには 500 エラーが含まれます。 404 エラー コードは "見つからない" ことを意味します。つまり、新しいワークスペース用のストレージ領域がまだプロビジョニング中であることを示します。 次回の再試行時に、データはワークスペースに正常に書き込まれます。 HTTP エラー 403 は、アクセス許可または資格情報の問題を示している可能性があります。 403 エラーには、イシューの解決に役立つ詳しい情報が含まれています。 4000 Service Connector DNS 名前解決に失敗しました。 コンピューターで、サービスにデータを送信するときに使用されるインターネット アドレスを解決できませんでした。 このイシューは、コンピューター上の DNS リゾルバーの設定、不適切なプロキシの設定、またはプロバイダーとの一時的な DNS の問題の場合があります。 ときおり発生する場合は、一時的なネットワーク関連の問題が原因の可能性があります。 4001 Service Connector サービスへの接続に失敗しました。 このエラーは、エージェントが Azure Monitor サービスと直接またはファイアウォールやプロキシ サーバーを介して通信できない場合に発生する可能性があります。 エージェントのプロキシ設定、またはネットワーク ファイアウォールやプロキシでコンピューターからサービスへの TCP トラフィックが許可されていることを確認します。 4002 Service Connector クエリに応答してサービスから HTTP 状態コード 403 が返されました。 サービスの状態についてサービス管理者に確認してください。 クエリは後で再試行されます。 このエラーは、エージェントの初期登録フェーズ中に書き込まれます。 https://<workspaceID>.oms.opinsights.azure.com/AgentService.svc/AgentTopologyRequest のような URL が表示されます。 403 エラー コードは "禁止" を意味し、誤って入力されたワークスペース ID またはキーが原因で発生する可能性があります。 コンピューターの日付と時刻も正しくない可能性があります。 時刻が現在の時刻より 15 分後または前である場合、オンボードは失敗します。 このイシューを修正するには、Windows コンピューターの日付や時間を更新します。
データ収集の問題
エージェントがインストールされ、構成された 1 つまたは複数のワークスペースに報告されると、有効なものとそのコンピューターをターゲットにしているものに応じて、構成の受信、パフォーマンス、ログ、またはその他のデータのサービスへの収集または転送が停止します。 以下のこと確認する必要があります。
- ワークスペースで使用できないのは特定のデータ型ですか、それともすべてのデータですか。
- データ型はソリューションによって指定されていますか、それともワークスペース データ収集の構成の一部として指定されていますか。
- 何台のコンピューターが影響を受けますか。 ワークスペースに報告しているのは 1 台のコンピューターですか、それとも複数のコンピューターですか。
- それは機能していて 1 日の特定の時点で停止しましたか、それとも収集が実行されたことはありませんでしたか。
- 使用しているログ検索クエリの構文は正しいですか。
- エージェントで Azure Monitor からその構成を受信したことはありますか。
トラブルシューティングの最初の手順は、コンピューターからハートビート イベントが送信されているかどうかを確認することです。
Heartbeat
| where Computer like "<ComputerName>"
| summarize arg_max(TimeGenerated, * ) by Computer
クエリから結果が返される場合は、特定のデータ型の収集とサービスへの転送が行われていないかどうかを判断する必要があります。 このイシューは、更新された構成をエージェントがサービスから受信していないこと、またはエージェントの正常な動作を妨げる他の何らかの症状の可能性があります。 さらにトラブルシューティングを行うには、次の手順を実行します。
コンピューターで管理者特権のコマンド プロンプトを開き、
net stop healthservice && net start healthservice
を入力してエージェント サービスを再起動します。Operations Manager イベント ログを開き、イベント ソースの HealthService から イベント ID の 7023、7024、7025、7028、1210 を検索します。 これらのイベントは、エージェントが Azure Monitor から構成を正常に受信しており、コンピューターをアクティブに監視していることを示します。 イベント ID 1210 のイベントの説明では、最後の行に、エージェントの監視範囲に含まれるすべての解決策と分析情報も指定されます。
数分お待ちください。 クエリ結果または視覚化に想定されるデータが表示されない場合は、Operations Manager イベント ログの解決策と分析情報のどちらのデータを表示しているかに応じて、イベント ソース HealthService と Health Service Modules を検索します。 イベント レベル "警告" と "エラー" でフィルター処理して、次の表のイベントが書き込まれたかどうかを確認します。
イベント ID source 説明 解決方法 8000 HealthService このイベントは、パフォーマンス、イベント、または収集されたその他のデータ型に関連するワークフローを、ワークスペースへの取り込みのためにサービスに転送できないかどうかを示します。 ソース HealthService のイベント ID 2136 がこのイベントと共に書き込まれ、エージェントがサービスと通信できないことを示す場合があります。 考えられる理由としては、プロキシと認証設定の構成が不適切、ネットワークが停止している、またはネットワーク ファイアウォールまたはプロキシがコンピューターからサービスへの TCP トラフィックを許可していないことが考えられます。 10102 と 10103 Health Service Modules ワークフローでデータ ソースを解決できませんでした。 このイシューは、指定されたパフォーマンス カウンターまたはインスタンスがコンピューター上に存在しないか、ワークスペース データ設定で誤って定義されている場合に発生する可能性があります。 これがユーザー指定のパフォーマンス カウンターである場合は、指定されている情報が正しい形式であり、ターゲット コンピューター上に存在することを確認します。 26002 Health Service Modules ワークフローでデータ ソースを解決できませんでした。 このイシューは、指定された Windows イベント ログがコンピューター上に存在しない場合に発生する可能性があります。 このイベント ログがコンピューターに登録されていないと予想される場合、このエラーは無視しても問題ありません。 それ以外で、これがユーザー指定のイベント ログである場合は、指定された情報が正しいことを確認してください。
以前の Microsoft Monitoring Agent に関するピン留めされた証明書の問題 - 破壊的変更
ルート CA 変更の概要
2023 年 6 月 30 日以降、Log Analytics バックエンドは、古いルート証明書を参照する MMA からの接続を受け入れなくなります。 これらの MMA は、Winter 2020 リリースより前 (Log Analytics エージェント) および SCOM 2019 UR3 より前 (SCOM) の古いバージョンです。 Bundle: 10.20.18053 および Extension: 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 通信に影響します。 これは、この古いルート CA が廃止された後、それを使用する MMA からの接続を Log Analytics が受け入れなくなることを意味します。
"ソリューション製品"
Microsoft Monitoring Agent を個人的にインストールしていない場合でも、この破壊的変更の通知を受け取った可能性があります。 これは、さまざまな Azure 製品で Microsoft Monitoring Agent が利用されているためです。 これらの製品のいずれかを使用している場合は、Windows Log Analytics エージェントを利用しているために、影響を受ける可能性があります。 以下にリンクを示す製品については、最新のエージェントにアップグレードする必要がある特定の手順が存在する場合があります。
- VM Insights
- System Center Operations Manager (SCOM)
- System Center Service Manager (SCSM)
- Microsoft Defender for Server
- Microsoft Defender for Endpoint
- Azure Sentinel
- Azure Automation エージェント ベースのハイブリッド worker
- Azure Automation 変更履歴とインベントリ
- Azure Automation の Update Management
中断されるエージェントの識別と回避
エージェントの数が限られているデプロイの場合は、これらの管理手順を使用してノードごとにエージェントをアップグレードすることを強くお勧めします。
複数のノードを含むデプロイのために、Microsoft では、サブスクリプションごとに影響を受ける中断される 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"
}
}