Close-SMBOpenFile 抛出错误并且未被 try-catch 捕获

Close-SMBOpenFile throws error and isn't caught in try-catch

我们在脚本执行模式下使用 TeamCity powershell 作为具有快照和工件依赖项的管道的一部分。我们有一个相当强大的系统,并且已经使用这个特定的过程几年了,所以这不是我第一次调试的全新代码。可悲的是。它通常会工作,直到它随机不工作。当错误确实发生时,TeamCity Agent 是不同的。

我们流程的这一部分执行一些代码部署和一些日志备份。为了完全备份,我们必须确保文件不会被 QA 或开发人员在他们办公桌前查看日志并可能以读写模式或类似模式打开。因为他们会从 laptop/desktops 中打开它们,所以它们自然是 SMB 共享。所以我们在下面有这个函数应该关闭在给定服务器上打开的文件。我说应该,因为每隔一段时间它就会抛出这个错误,而且我似乎无法捕获它(甚至在本地)或抑制它,所以它破坏了 TeamCity 运行。 (我在代码是专有名称或专有输出的任何地方使用 ...SNIP 进行了匿名处理)

您实际上可以通过导航到 \yourhostname\c$\somefilepath\somefile 在您的机器上测试它,您会看到它会显示文件已打开。一旦您通读代码并查看它在做什么,它应该不会在您的机器上失败,但是如果您取出所有 "precautions",您可能会在本地重现错误。

function Close-SMBApplicationLocks {
<#
.SYNOPSIS
    Closes Active SMB Sessions for Default or User Supplied Paths

.DESCRIPTION
    This function is used to prevent interruption to deployments by closing any SMB locks
    in application paths.  Defaults to closing sessions in folders matching regex
    ...SNIP

.PARAMETER Paths
    [string[]] A string array of paths or path segments to match sessions against.

.EXAMPLE
    Close-SMBApplicationLocks

...SNIP

.EXAMPLE
    Close-SMBApplicationLocks -Paths @("TEMP")

...SNIP
#>
    [CmdletBinding()]
    param(
        [Alias("SharePaths")]
        [Parameter(Mandatory=$false)]
        [string[]]$Paths
    )

    $pathsToUse = Test-IsNull ($Paths -join "|") "...SNIP"
    Write-Verbose ("Looking for SMB Sessions Matching Path: {0}" -f $pathsToUse)

    $smbSessions = @(Get-SmbOpenFile | Where-Object {$_.Path -match $pathsToUse})

    if ((Test-IsCollectionNullOrEmpty $smbSessions)) {
        Write-Host ("No Matching SMB Sessions Found")
        return
    }

    Write-Verbose "Found $($smbSessions.Count) Matching SMB Sessions"

    $uniqueFileIds = ($smbSessions).FileId | Sort-Object -Unique

    foreach ($fileId in $uniqueFileIds) {
        $session = @($smbSessions | Where-Object { $_.FileId -eq $fileId })[0]

        $sessionId = $session.SessionId
        $username = $session.ClientUserName
        $path = $session.Path

        Write-Verbose "Closing FileId $fileId on SMB Session $sessionId for user $username in path $path"

        try {
            if ($null -ne (Get-SmbOpenFile -FileId $fileId)) {
                ## Yes this is FOUR ways to suppress output. 
                ## Microsoft has proven remarkably resilient at showing an error here.
                ## the ErrorAction Continue still throws an error in TeamCity but not locally
                ## The try catch doesn't catch
                ## The Out-Null is because on the off chance the redirect works on the output, it shouldn't show the faux-error
                ## The output redirection is because this error isn't written to "standard error"
                ## TeamCity seems to be not honoring this output redirection in the shell it's running under to execute this block
                (Close-SmbOpenFile -FileId $fileId -Force -ErrorAction Continue *>&1) | Out-Null
                ## Run this line instead of the above to actually see the error pretty frequently, by my testing
                ## Close-SmbOpenFile -FileId $fileId -Force
            }
        } catch {
            $errorMessage = $_.Exception.Message
            Write-Warning "An Error Occurred While Trying to Close Session $sessionId : $errorMessage"
        }
    }
}

我们最初是通过会话,但我更改为这个 $fileId 版本的代码,看看我是否可以用 unique 等清理它。那些似乎没有改进。

我们可以很好地做到 Get-SMBOpenFile | Where-Object <pathmatch> | Close-SMBOpenFile(参见此处的示例 https://serverfault.com/questions/718875/close-locked-file-in-windows-share-using-powershell-and-openfiles and here https://community.spiceworks.com/topic/2218597-issue-with-close-smbopenfile)但是如您所见,我们想要记录我们正在关闭它,以防我们发现出现问题并且这有助于我们理解什么。

这是我要解决的错误:

[Clearing File Locks] No MSFT_SMBOpenFile objects found with property 'FileId' equal to '825975900669'.  Verify the value of the property 
[Clearing File Locks] and retry.
[Clearing File Locks] At C:\Program Files\WindowsPowerShell\Modules\...SNIP.psm1:2566 char:34
[Clearing File Locks] +         $jobs | ForEach-Object { Receive-Job -Job $_ }
[Clearing File Locks] +                                  ~~~~~~~~~~~~~~~~~~~
[Clearing File Locks]     + CategoryInfo          : ObjectNotFound: (825975900669:UInt64) [Get-SmbOpenFile], CimJobException
[Clearing File Locks]     + FullyQualifiedErrorId : CmdletizationQuery_NotFound_FileId,Get-SmbOpenFile
[Clearing File Locks]     + PSComputerName        : localhost
[Clearing File Locks]  
[Clearing File Locks] Process exited with code 1

但问题是,就在我删除之前,我再次检查文件是否打开,对吧?所以我说 "does this exist? Yes? Close it" 然而,我得到了这个对我来说毫无意义的错误。

我曾尝试想出其他方法来处理返回的对象,以确保我需要删除文件,或者如果有内容说 "this should be skipped" 但我想不出任何东西。

既然我在这里似乎没有选择,有没有我没有考虑过的替代方法?某种 CIMInstance 命令?如果有的话,我显然已经雪盲了。这 运行 在计算机本地执行,而不是跨会话。

Someone in my org finally noticed that the error does say Get-SmbOpenFile with the FileId parameter is the failure, so that has to be the same redirection error. At this point it looks like I may have an answer.

Snowblindness sucks

相关机器详情备注:

PS Z:\git\...SNIP> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.17763.1007
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.17763.1007
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

PS Z:\git\...SNIP> Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version, ServicePackMajorVersion, OSArchitecture, CSName, WindowsDirectory


Caption                 : Microsoft Windows 10 Enterprise LTSC
Version                 : 10.0.17763
ServicePackMajorVersion : 0
OSArchitecture          : 64-bit
CSName                  : ...SNIP
WindowsDirectory        : C:\Windows

但这也是 运行在 Windows 服务器环境中进行的。相同版本的 PowerShell。所有服务器上的最新 Windows 补丁等。我知道,我们还没有将机队转移到 2019 数据中心,但据我所知,我们在整个机队 production/testing 中有一些奇怪的 800 台服务器,这些事情当然需要时间。如果 2016 年是问题,那就是问题。

PS Z:\git\...SNIP> Get-CimInstance Win32_OperatingSystem -ComputerName ...SNIP | Select-Object Caption, Version, ServicePackMajorVersion, OSArchitecture, CSName, WindowsDirectory


Caption                 : Microsoft Windows Server 2016 Datacenter
Version                 : 10.0.14393
ServicePackMajorVersion : 0
OSArchitecture          : 64-bit
CSName                  : ...SNIP
WindowsDirectory        : C:\Windows

也许我的解决方案是让 TeamCity 接受输出重定向?是 Server 2016 不支持输出重定向吗?这只是试图可靠地关闭这些连接的白日梦吗?是否有我不想检查的文件系统版本?

当我尝试在 \mymachine\c$\temp\temp.txt 创建一个文件并打开它时,这就是我得到的结果(请注意,我只是使用记事本打开文件,所以没有正在进行的锁定)

PS Z:\git\devops_powershell> Get-SMBOpenFile

FileId        SessionId     Path    ShareRelativePath ClientComputerName   ClientUserName
------        ---------     ----    ----------------- ------------------   --------------
1065151889485 1065151889409 C:\                       ...SNIP              ...SNIP
1065151889489 1065151889409 C:\                       ...SNIP              ...SNIP
1065151889613 1065151889409 C:\temp temp              ...SNIP              ...SNIP
1065151889617 1065151889409 C:\temp temp              ...SNIP              ...SNIP
1065151889833 1065151889409 C:\temp temp              ...SNIP              ...SNIP


PS Z:\git\...SNIP> Get-SmbOpenFile -FileId 1065151889833 | Select-Object -Property *


SmbInstance           : Default
ClientComputerName    : ...SNIP
ClientUserName        : ...SNIP
ClusterNodeName       :
ContinuouslyAvailable : False
Encrypted             : False
FileId                : 1065151889833
Locks                 : 0
Path                  : C:\temp
Permissions           : 1048736
ScopeName             : *
SessionId             : 1065151889409
ShareRelativePath     : temp
Signed                : True
PSComputerName        :
CimClass              : ROOT/Microsoft/Windows/SMB:MSFT_SmbOpenFile
CimInstanceProperties : {ClientComputerName, ClientUserName, ClusterNodeName, ContinuouslyAvailable...}
CimSystemProperties   : Microsoft.Management.Infrastructure.CimSystemProperties



PS Z:\git\...SNIP> Get-SmbOpenFile -FileId 1065151889617 | Select-Object -Property *


SmbInstance           : Default
ClientComputerName    : ...SNIP
ClientUserName        : ...SNIP
ClusterNodeName       :
ContinuouslyAvailable : False
Encrypted             : False
FileId                : 1065151889617
Locks                 : 0
Path                  : C:\temp
Permissions           : 1048705
ScopeName             : *
SessionId             : 1065151889409
ShareRelativePath     : temp
Signed                : True
PSComputerName        :
CimClass              : ROOT/Microsoft/Windows/SMB:MSFT_SmbOpenFile
CimInstanceProperties : {ClientComputerName, ClientUserName, ClusterNodeName, ContinuouslyAvailable...}
CimSystemProperties   : Microsoft.Management.Infrastructure.CimSystemProperties

我应该只关注 Locks -gt 0?

的情况吗

由于 Get-SmbOpenFile -FileId $fileId 失败,我们似乎已经缩小了根本原因的范围。这可能与多个 4 分开的并发文件列表有关,例如,在上面的最后一个示例中,当 1065151889485 关闭时,它 "closes" 1065151889489 也关闭,然后当我们尝试为此循环迭代时值,它找不到它,因此出错。

PS Z:\git\devops_powershell> Get-SMBOpenFile

FileId        SessionId     Path    ShareRelativePath ClientComputerName   ClientUserName
------        ---------     ----    ----------------- ------------------   --------------
1065151889485 1065151889409 C:\                       ...SNIP              ...SNIP
1065151889489 1065151889409 C:\                       ...SNIP              ...SNIP
1065151889613 1065151889409 C:\temp temp              ...SNIP              ...SNIP
1065151889617 1065151889409 C:\temp temp              ...SNIP              ...SNIP
1065151889833 1065151889409 C:\temp temp              ...SNIP              ...SNIP

我打算在早上更改 Get-SmbOpenFile -FileId $fileId 行并用 "error bypass" 废话进行测试,看看那里也会发生什么。或者只是结帐并重试。

我仍然非常困惑 try-catch 如何不主动捕获抛出的错误。如果是这样,我只会有一个写警告而不是我现在的结束过程。