【问题标题】:PowerShell module not dot-sourcing / importing functions as expectedPowerShell 模块未按预期提供点源/导入功能
【发布时间】:2017-12-11 18:21:07
【问题描述】:

更新 1: 最初,我发布了这个标题:“脚本忽略 PowerShell 模块中的错误处理”,因为这是当前的问题,但它似乎更像是一个模块问题,所以我重命名了标题。

更新 2: 在发表让我质疑 Azure cmdlet 的评论之后,我使用最基本的脚本(添加到模块)进行了测试,结果是相同的,因为错误没有传递给调用脚本,但是添加了 @987654321 @ to Get-Service 确实返回了一些我可以用来处理错误的东西(WriteErrorException 除外):

function Test-MyError($Variable)
{
    Try
    {
        Get-Service -Name $variable -ErrorAction Stop -ErrorVariable bar
        #Get-AzureRmSubscription -SubscriptionName $variable -ErrorAction Stop
    }
    Catch
    {
        Write-Error $error[0]
        $bar
    }
}

返回:

Test-MyError "Foo"

Test-MyError : Exception of type 'Microsoft.PowerShell.Commands.WriteErrorException' was thrown.
At line:3 char:1
+ Test-MyError "Foo"
+ ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Test-MyError

The running command stopped because the preference variable "ErrorActionPreference" or common parameter is set to Stop: Cannot find any service with service name 'foo'.

但是,如果我在 ISE 中运行“Test-MyError”,然后调用该函数,我会得到:

Test-MyError "Foo"

Test-MyError : Cannot find any service with service name 'Foo'.
At line:3 char:1
+ Test-MyError "Foo"
+ ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Test-MyError

The running command stopped because the preference variable "ErrorActionPreference" or common parameter is set to Stop: Cannot find any service with service name 'Foo'.

所以我不确定在 ISE 中运行“Test-MyError”并调用它时会发生什么,反对它在 PSM1 文件中被点源化然后调用它?

我现在必须使用-ErrorVariable 并处理它吗?

原问题: 我在一个模块中有两个函数:Get-Subscription 和 Get-AllSubscriptions。每个函数都位于其自己的 PS1 文件中,并且 PSM1 文件对它们进行点源。该模块看起来不错,因为可以使用 intelisense 访问模块脚本并且模块加载没有问题。我在很多模块中都使用过这种结构,之前没有遇到过这个问题。 (虽然我想知道 MS 是否改变了模块在 PS 5.1 中的工作方式,因为我注意到使用 FunctionsToExport='x','y','Z'Export-ModuleMember 的行为似乎与以前不同。)

Get-AllSubscriptions 调用 Get-Subscription。

如果我没有登录到 Azure,Get-Subscription 应该抛出一个错误,并提示我登录。如果我从 Get-Subscription.ps1 调用 Get-Subscription,这将按预期工作。

但是,当我从新的 PS1 文件 Get-AllSubscriptions 或 powershell 控制台调用 Get-Subscription 时,它不起作用。它一直遍历 do..until 循环,而没有像我期望的那样“处理”错误。在每次迭代中,它似乎都会抛出一个通用错误:

Get-Subscription : Exception of type 'Microsoft.PowerShell.Commands.WriteErrorException' was thrown.

但是,我确实看到了最后一个错误,Get-Subscription : Unable to find requested subscription after 3 login attempts.

如果我在 ISE 中执行 Get-Subscription,然后在新 PS1 文件中或从 Get-AllSubscriptions 中调用 Get-Subscription,它会按预期工作,但是,一旦我重新导入模块 (Import-Module AzureVnetTools -Force -Verbose),它就会运行回到不正确的行为。

如果我在调用者脚本中点源获取订阅,它可以工作,但为什么呢?这就是模块的 PSM1 应该发生的情况。

谁能帮我找出我做错了什么?

(PS 5.1,Windows 7)

获取订阅:

function Get-Subscription
{
    [cmdletbinding()]
    Param
    (
        [string]$SubscriptionName,
        [string]$UserName,
        [string]$code
    )

    $c=1
    Write-Verbose "Checking access to '$SubscriptionName' with user '$UserName'..."
    Do
    {
        Write-Verbose "Attempt $c"
        Try
        {
            $oSubscription = Get-AzureRmSubscription -SubscriptionName $SubscriptionName -ErrorAction Stop -WarningAction SilentlyContinue

            Write-Verbose "Subscription found: $($oSubscription.SubscriptionName)."
        }
        Catch
        {
            if($error[0].Exception.Message -like "*Please verify that the subscription exists in this tenant*")
            {
                Write-Verbose "Cannot find subscription '$SubscriptionName' with provided credentials."
                $account = Login-AzureRmAccount -Credential (Get-Credential -UserName $Username -Message "Subscription '$SubscriptionName' user' password:")
            }
            elseif($error[0].Exception.Message -like "*Run Login-AzureRmAccount to login*")
            {
                Write-Verbose "No logged in session found. Please log in."
                $account = Login-AzureRmAccount -Credential (Get-Credential -UserName $Username -Message "Subscription '$SubscriptionName' user' password:")
            }
            else
            {
                Write-Error $error[0]
            }
        }
        $c++
    }
    until(($oSubscription) -or ($c -eq 4))

    if($c -eq 4)
    {
        Write-Error "Unable to find requested subscription after $($c-1) login attempts."
        break
    }
    $oSubscription | Add-Member -MemberType NoteProperty -Name Code -Value $code
    $oSubscription
}

获取所有订阅:

function Get-AllSubscriptions
{
    [cmdletbinding()]
    param
    (
        [string]$MasterSubscription,
        [string]$MasterSubscriptionCode,
        [string]$MasterSubscriptionUsername,
        [string]$ChildSubscription,
        [string]$ChildSubscriptionCode,
        [string]$ChildSubscriptionUsername
    )

    Write-Verbose "Getting all subscriptions..."

    $oAllSubscriptions = @()

    $oMasterSubscription = Get-Subscription -SubscriptionName $MasterSubscription -UserName $MasterSubscriptionUsername -code $MasterSubscriptionCode -Verbose

    $oChildSubscription = Get-Subscription -SubscriptionName $ChildSubscription -UserName $ChildSubscriptionUsername -code $ChildSubscriptionCode -Verbose

    $oAllSubscriptions = ($oMasterSubscription,$oChildSubscription)
    $oAllSubscriptions

}

测试:

$splat2 = @{
    SubscriptionName = "SomeSubscription"
    Code = "S02"
    Username = "some.user@somewhere.com"
}

#Write-Output "Dot-source:"
#. "D:\Temp\PS.Modules\AzureVnetTools\functions\public\Get-Subscription.ps1"

Get-Subscription @splat2 -verbose

输出:

Get-Subscription @splat2 -verbose
VERBOSE: Checking access to 'SomeSubscription' with user 'some.user@somewhere.com'...
VERBOSE: Attempt 1
Get-Subscription : Exception of type 'Microsoft.PowerShell.Commands.WriteErrorException' was thrown.
At line:7 char:1
+ Get-Subscription @splat2 -verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-Subscription

VERBOSE: Attempt 2
Get-Subscription : Exception of type 'Microsoft.PowerShell.Commands.WriteErrorException' was thrown.
At line:7 char:1
+ Get-Subscription @splat2 -verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-Subscription

VERBOSE: Attempt 3
Get-Subscription : Exception of type 'Microsoft.PowerShell.Commands.WriteErrorException' was thrown.
At line:7 char:1
+ Get-Subscription @splat2 -verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-Subscription

Get-Subscription : Unable to find requested subscription after 3 login attempts.
At line:7 char:1
+ Get-Subscription @splat2 -verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-Subscription

AzureVnetTools.psm1

#Get public and private function definition files.
$Public  = @( Get-ChildItem -Path $PSScriptRoot\Functions\Public\*.ps1 -ErrorAction SilentlyContinue )
$Private = @( Get-ChildItem -Path $PSScriptRoot\Functions\Private\*.ps1 -ErrorAction SilentlyContinue )

#Dot source the files
Foreach($import in @($Public + $Private))
{
    #write-error $import.fullname
    Try
    {
        #Write-Host "Dot-sourcing file: $($import.fullname)." 
        . $import.fullname
    }
    Catch
    {
        Write-Error -Message "Failed to import function $($import.fullname): $_"
    }
}

Export-ModuleMember -Function $Public.Basename

AzureVnetTools.psd1(相关部分):

FunctionsToExport = '*'

【问题讨论】:

  • 听起来像 Write-Error $Error[0] 正在评估由于未处理的异常。更改为Write-Host $Error[0] 看看是否会打印一些有用的东西。
  • @woter324 您的帐户是什么类型的? Microsoft 帐户或 Azure AD 帐户?你检查你的订阅名称正确吗?
  • @gms0ulman 谢谢,尝试了你的建议,但没有区别。
  • @walter - MSFT 这是一个 AAD 帐户,但我认为这不是问题所在。订阅正确。
  • @woter324 您的 Azure PowerShell 版本是什么?我在我的实验室测试你的脚本,我没有遇到你的错误。您可以使用 Get-Module -ListAvailable -Name Azure -Refresh 检查您的 Azure PowerShell 版本。最新版本是 4.10。

标签: powershell azure-powershell


【解决方案1】:
-ErrorAction Stop -WarningAction SilentlyContinue

这是引发的警告而不是错误吗?

【讨论】:

  • 感谢您的建议,我确实删除了 -warningAction,但没有任何变化。
【解决方案2】:

所以我的具体问题是我依赖于处理错误并以此为基础做一些事情。问题是由 PowerShell 的 Write-Error 工作(或不工作)方式引起的,正如我从 @Alek 给出的回复 here 中了解到的那样。

它根本没有将实际错误传递回调用脚本。正如@Alex 建议的那样,我将Write-Error 替换为$PSCmdlet.WriteError()。虽然这并不完全奏效。

Catch{} 块中,我随后将$error[0] 更改为$_,并将完整错误返回给调用脚本/函数。

我更进一步,写了一个可重用的函数,添加到我的模块中:

function Write-PsError
{
    [cmdletbinding()]
    Param
    (
        [Exception]$Message,
        [Management.Automation.ErrorCategory]$ErrorCategory = "NotSpecified"
    )

    $arguments = @(
            $Message
            $null #errorid
            [Management.Automation.ErrorCategory]::$ErrorCategory
            $null

            )

    $ErrorRecord = New-Object -TypeName "Management.Automation.ErrorRecord" -ArgumentList $arguments
    $PSCmdlet.WriteError($ErrorRecord)

}

目前似乎运作良好。我特别喜欢智能感知获取所有错误类别的方式。不确定 ISE(PS 5.1 / Win 7)是什么或如何做到这一点的。我以为我必须添加自己的动态参数。

HTH。

【讨论】:

    猜你喜欢
    • 2013-01-30
    • 1970-01-01
    • 1970-01-01
    • 2019-10-10
    • 2013-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多