PowerShell 模块的内部分发与更新 ── PSResourceGet 与内部仓库

· · PowerShell, 模块, 分发, 版本管理, 运维改善, 可维护性, 信息系统, 自动化

「写了个方便的脚本,就放到共享文件夹里了」── 从那一刻起,维护负债就开始悄悄累积。有人复制后在本地进行了改造,原始文件即使修好了也不会同步到那份改造版本,谁也说不清哪个版本正在哪里运行。几年之后,共享文件夹里就会并排出现 汇总.ps1汇总_v2.ps1汇总_修正版_最新.ps1

在 PowerShell 的世界里,这个问题的答案很明确。做成模块、加上版本号、从仓库分发。仅凭这一点,就能回答「装的是哪个版本」「更新之后是否会送达给所有人」这两个问题。而且从 PowerShell 7.4 开始,实现这一点所需的机制(PSResourceGet)从一开始就已经内置在其中。

本文将以不搭建专用服务器的现实构成为前提,讲解如何把公司内部共享的脚本模块化、搭建内部仓库并进行分发与更新。关于函数模块化本身,先阅读《PowerShell 脚本的参数设计与模块化》会更容易理解。

目标读者・前提环境

项目 内容
目标读者 正在通过共享文件夹分发 .ps1 的信息系统・运维负责人
目标版本 Windows PowerShell 5.1 与 PowerShell 7.x 两者。不过 PSResourceGet 只在 PowerShell 7.4 及以后才随附,在 5.1 中,分发方和使用方都需要提前安装(第 5 章)1
与现有环境并存 PSResourceGet 可以与传统的 PowerShellGet 2.2.5 并存。可以在不改写现有脚本的情况下引入1
示例的验证环境 本文末尾提供下载的示例代码是在 PowerShell 7.6 中运行验证的
所需权限 创建并设置仓库用共享文件夹、以及以 -Scope AllUsers 进行安装,都需要管理员权限

1. 先说结论

  • PSResourceGet(Microsoft.PowerShell.PSResourceGet)已随附在 PowerShell 7.4 中。由于它可以与传统的 PowerShellGet 2.2.5 并存,因此能在不破坏现有脚本的情况下使用。Windows PowerShell 5.1 中没有随附,因此使用方和分发方都需要提前引入(下一章)。1
  • 内部仓库可以从文件共享开始搭建。只需在 Register-PSResourceRepository 中指定 UNC 路径即可。不需要专用服务器。2
  • 请把清单(.psd1)当作必需品。没有版本号,就既无法更新,也无法排查故障。3
  • FunctionsToExport 不要使用通配符,而要用数组明确列出。这样命令探索会更快,也能防止内部函数被意外公开。3
  • 版本号请采用语义化版本管理(Semantic Versioning)。如果无法用主版本号来体现破坏性变更,使用方就无法放心地进行更新。4
  • 发布用 Publish-PSResource,获取用 Install-PSResource,更新用 Update-PSResource56
  • 模块的探索路径在 5.1 和 7 中是不同的。如果不理解 $env:PSModulePath 的差异,就会出现「明明装了却找不到」的情况。7
  • 如果执行策略是 AllSigned,就必须对每个脚本文件进行 Authenticode 签名。目录签名(catalog signing)用于验证软件包的完整性,并不满足执行策略的要求。8
  • 对于无人值守执行所依赖的模块,自动更新要谨慎对待。先验证后再有计划地升级,这样的运维方式更安全。

本文所构建的机制,整体上会呈现出下面这样的形态。

Publish-PSResourceInstall-PSResourceInstall-PSResource -Scope AllUsers开发侧信息系统人员编写模块测试与静态分析Pester / PSScriptAnalyzer内部仓库以 UNC 路径指定的文件共享,或NuGet 兼容源使用侧电脑Find / Install / Update-PSResource使用侧服务器夜间批处理等无人值守执行

图中箭头上标注的命令,就是本文所要处理的核心内容。仓库需要由分发方和使用方双方各自在最初只用 Register-PSResourceRepository 注册一次(第 5 章),之后就只靠发布、获取、更新这几条命令来运转(第 6 章)。只有分发负责人拥有写入权限,这是本图中唯一的安全边界。

2. “共享文件夹里的 ps1” 存在的问题

首先,明确一下本文要解决的是什么问题。

症状 根本原因
不知道正在运行的是哪个版本 不存在版本号这个概念
即使修复了也不会同步给所有人 每个人都各自持有一份副本
不知道是谁在使用 没有获取记录留存(※如后文所述,唯独这一点需要根据分发方式来选择)
只有部分环境会出问题 依赖关系(所需模块・PS 版本)未被声明
想修复却看不清影响范围 公开的函数和内部函数没有区分

模块化与仓库分发,对上面这四项都能直接起效。但唯独「谁在使用」这一点,取决于所采用的分发机制。下文将要介绍的文件共享仓库,虽然搭建轻松,但不会留下谁在何时获取过的记录(Get-InstalledPSResource 能了解到的,只是执行该命令的那台机器当下的状态)。如果想掌握使用情况,请并用以下方法之一。

3. 模块的最小构成

可分发模块的最小形态,由文件夹、.psm1.psd1 这三部分组成。

KsOps\
  KsOps.psd1     ← 清单(版本・公开函数・依赖关系)
  KsOps.psm1     ← 实现(或从 Public/Private 文件夹进行点源加载)
  Public\
    Get-KsShareUsage.ps1
    Invoke-KsArchive.ps1
  Private\
    ConvertTo-KsSize.ps1

清单可以用 New-ModuleManifest 生成模板,再填写所需的项目。3

$manifest = @{
    Path              = '.\KsOps\KsOps.psd1'
    RootModule        = 'KsOps.psm1'
    ModuleVersion     = '1.0.0'
    GUID              = [guid]::NewGuid().Guid
    Author            = '信息系统部'
    CompanyName       = '示例股份有限公司'
    Description       = '公司内部运维脚本通用模块(文件服务器盘点・归档)'
    PowerShellVersion = '5.1'
    CompatiblePSEditions = @('Desktop', 'Core')      # 同时用于 5.1 和 7 时
    # 不要使用通配符。只明确列出要公开的内容
    FunctionsToExport = @('Get-KsShareUsage', 'Invoke-KsArchive')
    CmdletsToExport   = @()
    VariablesToExport = @()
    AliasesToExport   = @()
    RequiredModules   = @()                          # 如有依赖,请在此声明
    Tags              = @('internal', 'operations')
    ProjectUri        = 'https://git.example.co.jp/it/ksops'
}
New-ModuleManifest @manifest

.psm1 可以按照固定套路来编写:加载 Public/Private 中的脚本,只导出公开的函数。

# KsOps.psm1
$public  = @(Get-ChildItem -Path "$PSScriptRoot\Public\*.ps1"  -ErrorAction SilentlyContinue)
$private = @(Get-ChildItem -Path "$PSScriptRoot\Private\*.ps1" -ErrorAction SilentlyContinue)

foreach ($file in @($public + $private)) {
    try   { . $file.FullName }
    catch { throw "模块加载失败: $($file.FullName) ── $_" }
}

# 公开的只有 Public 目录下的函数(要与清单中的声明保持一致)
Export-ModuleMember -Function $public.BaseName

不把 FunctionsToExport 写成通配符,理由有两个。一个是命令探索的性能:明确列出后,无需解析模块本体,就能判断出「哪个命令在哪里」。另一个是设计上的理由:如果内部辅助函数能从外部调用,它就会变成事实上的公开 API,之后就无法再更改了。3

上面的例子中,CompatiblePSEditions 同时声明了 Desktop(Windows PowerShell 5.1)和 Core(PowerShell 7),但仅凭声明并不会让它在两边都能运行。在 5.1 中使用 PSResourceGet 本身所需的准备工作整理在第 5 章,因 5.1 与 7 的模块探索路径不同而导致「明明装了却找不到」的问题则整理在第 7 章。如果打算同时兼容两个版本进行分发,请先阅读这两章。

4. 版本管理的确定方法

使用方能否放心地进行更新,取决于版本号的编排方式。请采用语义化版本管理(主版本号.次版本号.修订号),并务必用主版本号来体现破坏性变更4

变更内容 应提升的部分
参数名变更、删除函数、返回值形式变更 主版本号(1.2.3 → 2.0.0)
添加函数或参数(现有功能照常运行) 次版本号(1.2.3 → 1.3.0)
仅修复缺陷 修订号(1.2.3 → 1.2.4)

如果想分发验证用的版本,可以使用预发行版(prerelease)。在清单的 PrivateData.PSData.Prerelease 中设置类似 beta1 这样的字符串(与版本号之间的连字符会自动添加,变成 1.3.0-beta1),这样在常规获取中就不会被下载到,只有明确指定 -Prerelease 时才会被安装。字符串中只能使用 ASCII 字母数字和连字符,不能使用句点或 +4

关于破坏性变更的处理方式,接口设计的思路可以作为参考(参见《DLL・COM 接口的向后兼容性》)。

5. 搭建内部仓库 ── 文件共享就已足够

PSResourceGet 可以把文件共享上的文件夹当作仓库来使用2 这是引入成本最低的构成方式。

首先确认一下前提条件。PowerShell 7.4 及以后版本已经随附,但 Windows PowerShell 5.1 中没有。要在 5.1 中使用接下来出现的命令(Register-PSResourceRepository 等),请事先引入该模块。1

# 【仅限 Windows PowerShell 5.1】引入 PSResourceGet。
# 5.1 和 7 中加载的模块路径不同,请在实际使用的版本(Edition)中执行
if (-not (Get-Module -ListAvailable -Name Microsoft.PowerShell.PSResourceGet)) {
    Install-Module -Name Microsoft.PowerShell.PSResourceGet -Scope AllUsers -Force
}
# 【分发方・使用方都只需执行一次】注册内部仓库
# Trusted: 因为是公司内部分发物,所以当作已信任处理 / Priority: 优先于 PSGallery 进行搜索
$repo = @{
    Name     = 'KsInternal'
    Uri      = '\\fileserver\PSRepository'
    Trusted  = $true
    Priority = 10
}
Register-PSResourceRepository @repo

Get-PSResourceRepository | Format-Table Name, Uri, Trusted, Priority

共享文件夹的访问权限,请设置为「只有分发负责人可以写入,使用者只能读取」。如果这里设置得过于宽松,就会变成任何人都能把任意代码分发到全公司的通路。共享访问权限和 NTFS 访问权限两者都要设置(实际生效的权限以较严格的一方为准)。9

# 在文件服务器一侧执行(需要管理员权限)
$path = 'D:\PSRepository'
$null = New-Item -Path $path -ItemType Directory -Force

# 共享: 使用者只能读取,只有分发负责人能够写入
New-SmbShare -Name 'PSRepository' -Path $path `
    -ReadAccess 'EXAMPLE\Domain Users' -ChangeAccess 'EXAMPLE\模块分发负责人'

# NTFS: 不是"叠加",而是"用许可列表整体替换"。
# 由于 New-Item -Force 即使对既有文件夹也会执行成功,如果这个文件夹以前曾经
# 通过另一个共享公开过・或曾临时给某人赋予过更改权限,那么仅靠追加
# icacls /grant 是无法清除这条写入通路的。由于这是一个所有终端都会注册为
# Trusted 的仓库,残留下来的这一个人就能够向全公司分发代码
$acl = Get-Acl -Path $path
$acl.SetAccessRuleProtection($true, $false)          # 切断继承,且不继承已有的继承 ACE
foreach ($ace in @($acl.Access)) {                   # 直接授予的 ACE 也一并清除
    [void]$acl.RemoveAccessRuleSpecific($ace)
}

# 只保留这里列出的主体
$allow = @(
    @{ Id = 'EXAMPLE\模块分发负责人';     Rights = 'Modify' }         # 可以发布
    @{ Id = 'EXAMPLE\Domain Users';       Rights = 'ReadAndExecute' } # 只能获取
    @{ Id = 'BUILTIN\Administrators';     Rights = 'FullControl' }
    @{ Id = 'NT AUTHORITY\SYSTEM';        Rights = 'FullControl' }
)
foreach ($a in $allow) {
    $acl.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new(
        $a.Id, $a.Rights, 'ContainerInherit, ObjectInherit', 'None', 'Allow'))
}
# 如果把"清空"和"重新写入"分成两次 Set-Acl,就会出现谁都无法访问的瞬间。
# 要一次性完成应用
Set-Acl -Path $path -AclObject $acl

icacls $path        # 确认设置结果。用肉眼检查是否列出了意料之外的主体

请不要仅靠追加 icacls /grant 就了事。/grant 不会删除已有的 ACE。即使还有其他人拥有能写入这个文件夹的通路,它也会原样保留下来。9 而且,这个仓库的前提是所有终端都以 Trusted = $true 进行注册。放置在已信任仓库中的模块,无需确认就会被安装。这意味着残留下来的那一个人,就能向全公司的 PowerShell 执行环境分发任意代码,「只有分发负责人才能发布」这个前提到那一刻就崩塌了。

仅靠切断继承(SetAccessRuleProtection($true, $false))也是不够的。因为这个方法能处理的只是继承 ACE,直接授予该文件夹的 ACE 不在其处理范围之内9 因此要像上面那样,先把直接授予的部分也一并清除,再重新写入许可列表中的主体。如果是新建的文件夹,结果是一样的,但这属于只有在沿用既有文件夹时才会出问题的一类隐患,所以把它写进操作步骤里会更可靠。

「分发负责人」请设置为组,而不是个人账户。因负责人调动而导致模块无法更新,这种停摆情况确实会发生。如果使用需要身份验证的 NuGet 兼容源,请把发给使用方的凭据设置为只读权限,并与用于发布的凭据(API 密钥)分开。关于共享文件夹的权限设计本身,也可以参考《用 PowerShell 盘点文件服务器》。

如果想要更正式地运维,可以注册 Azure Artifacts、GitHub Packages 这类 NuGet 兼容源。对于需要身份验证的仓库,可以构建成从 SecretManagement 的保管库中引用凭据的形式(参见《PowerShell 凭据的安全处理》)。2

6. 发布・获取・更新

# 【分发方】发布模块
Publish-PSResource -Path .\KsOps -Repository 'KsInternal'

# 【使用方】搜索并安装
Find-PSResource -Name 'KsOps' -Repository 'KsInternal'
Install-PSResource -Name 'KsOps' -Repository 'KsInternal' -Scope CurrentUser

# 固定版本安装(生产服务器推荐这种方式)
Install-PSResource -Name 'KsOps' -Version '1.2.3' -Repository 'KsInternal' -Scope AllUsers

# 更新
Update-PSResource -Name 'KsOps' -Repository 'KsInternal'

# 确认已安装的内容(终究只是"这台终端"的状态)
Get-InstalledPSResource -Name 'KsOps' | Format-Table Name, Version, Repository, InstalledDate

# 如果想了解全公司的安装情况,可以在各终端执行并汇总
Invoke-Command -ComputerName $servers -ScriptBlock {
    Get-InstalledPSResource -Name 'KsOps' -ErrorAction SilentlyContinue |
        Select-Object Name, Version
} | Sort-Object PSComputerName

-Scope 的区分使用很重要。任务计划程序无人值守执行时所用的模块,需要安装到 AllUsers(或服务账户自身的环境)中。「明明在自己的环境中能运行,唯独夜间批处理却报 无法识别 cmdlet 而失败」,其典型原因就是安装到了 CurrentUser 作用域。67

另外,由于 -Scope AllUsers 会写入所有用户共用的位置(Program Files 下),如果不是以管理员身份启动的 PowerShell 就会失败7 如果在首次引入时出现「访问被拒绝」,请先确认是否已经提升了权限。

7. 模块的探索路径与 5.1/7 的差异

PowerShell 会从 $env:PSModulePath 中列出的文件夹中查找模块。Windows PowerShell 5.1 与 PowerShell 7 的默认路径是不同的7

版本(Edition) 用户作用域的默认路径
Windows PowerShell 5.1 %USERPROFILE%\Documents\WindowsPowerShell\Modules
PowerShell 7 %USERPROFILE%\Documents\PowerShell\Modules

对于两边都要用的模块,请在 CompatiblePSEditions 中同时声明 DesktopCore,并在两种环境中都测试过之后再分发。兼容性验证方面,PSScriptAnalyzer 的 PSUseCompatibleSyntax 很有帮助(参见《用 PSScriptAnalyzer 守护 PowerShell 脚本质量》)。

出现问题时,需要确认以下这三点。

$env:PSModulePath -split ';'                       # 探索路径
Get-Module -Name KsOps -ListAvailable              # 是否能找到・是哪个版本
(Get-Module KsOps -ListAvailable).ModuleBase       # 实际读取的位置

8. 签名与执行策略

签名有目的不同的两种机制,如果混淆使用,就会出现「明明签了名却无法执行」的情况。8

机制 保证的是什么 是否满足执行策略 AllSigned
Authenticode 签名(Set-AuthenticodeSignature) 各个脚本文件的发布者与完整性 满足(每个文件都需要签名)
目录签名(New-FileCatalog + 签名) 模块整体(软件包)的完整性 不满足

执行策略验证的,是即将加载的 .ps1 / .psm1 本身的 Authenticode 签名。即使对目录文件(.cat)进行了签名,其中的脚本文件仍然处于未签名状态,因此在 AllSigned 环境下,执行会被阻止。因此,如果正在使用 AllSigned,就必须对每个被执行的文件进行签名

# (1) AllSigned 环境下必须执行: 对每个被执行的文件进行 Authenticode 签名
Get-ChildItem .\KsOps -Recurse -Include *.ps1, *.psm1, *.psd1 | ForEach-Object {
    Set-AuthenticodeSignature -FilePath $_.FullName -Certificate $cert `
        -TimestampServer 'http://timestamp.digicert.com'
}

# (2) 此外,并用目录来检测整个软件包是否被篡改
New-FileCatalog -Path .\KsOps -CatalogFilePath .\KsOps\KsOps.cat -CatalogVersion 2
Set-AuthenticodeSignature -FilePath .\KsOps\KsOps.cat -Certificate $cert `
    -TimestampServer 'http://timestamp.digicert.com'

# 使用方进行的验证(与执行策略无关,用于确认分发物是否损坏)
Test-FileCatalog -Path .\KsOps -CatalogFilePath .\KsOps\KsOps.cat -Detailed

目录的价值在于能够确认「获取到的整套模块,是否与分发时完全一致」,PSResourceGet 一侧也提供了用于验证签名・目录的 -AuthenticodeCheck6 请把两者的职责区分开来理解:能否执行由 Authenticode 签名决定,分发物的完整性由目录负责

添加时间戳后,即使签名证书的有效期过期,签名依然保持有效。执行策略与签名运维的全貌,整理在《PowerShell 的执行策略与脚本签名》中。

9. 应作为运维规则事先确定的事项

比起技术,运维更重要。请至少确定以下事项。

  • 谁能够发布。拥有共享文件夹写入权限的人 = 能向全公司分发代码的人
  • 变更历史写在哪里。ReleaseNotes(清单中的 PrivateData.PSData)或 CHANGELOG 中,明确写出破坏性变更
  • 生产服务器是否固定版本。无人值守执行所依赖的模块,固定版本后再有计划地更新,这样更安全
  • 废弃的步骤。删除函数时,要提升主版本号,并提前预告弃用
  • 通过测试和 lint 之后再发布。理想情况下,发布应该从 CI 中进行(参见《用 Pester 完善 PowerShell 测试》)

10. 实务定式(判断表)

论点 选项 判断标准
分发形式 共享文件夹的 .ps1 / 模块 + 仓库 能否管理版本和更新,是分界点
模块管理 PowerShellGet 2.x / PSResourceGet 7.4 及以后已随附。新写内容用 PSResourceGet1
仓库 文件共享 / NuGet 兼容源 先从文件共享开始。需要身份验证・审计时改用兼容源2
清单 省略 / 必需 没有版本号,运维就无法成立3
公开函数 '*' / 用数组明确列出 出于探索性能和内部函数非公开的考虑3
安装位置 CurrentUser / 无人值守执行用 AllUsers 判断基准是服务账户能否看到7
生产环境的更新 自动更新 / 固定版本 + 有计划地更新 防止夜间批处理擅自用新版本运行
签名 无 / 各文件的 Authenticode 签名(+目录) AllSigned 环境下必须按文件签名。目录用于完整性验证8

11. 总结

  • 通过共享文件夹分发 .ps1,会导致版本・更新・依赖关系・公开范围全部变得无法管理。模块化与仓库分发可以解决这个问题。不过唯独「谁在使用」是个例外。文件共享仓库不会留下获取记录,因此请并用共享文件夹的读取审计、能获取下载统计信息的兼容源、或在各终端汇总 Get-InstalledPSResource 结果这三种方式之一。
  • 清单是必需的。FunctionsToExport 请用数组明确列出,不要公开内部函数。
  • 内部仓库只需把文件共享以 UNC 路径注册即可开始使用。写入权限的管理是实质上的安全边界。
  • 发布用 Publish-PSResource,获取用 Install-PSResource,更新用 Update-PSResource。无人值守执行所用的模块,要安装到 AllUsers 作用域中。
  • 5.1 和 7 的探索路径不同。如果要同时兼容两者,请声明 CompatiblePSEditions,并在两种环境中都进行测试。
  • AllSigned 环境下,每个脚本文件都需要 Authenticode 签名。目录签名用于验证分发物的完整性,与能否执行是两回事。请务必添加时间戳。

示例代码下载

本文中出现的代码,已经整理成可以直接运行的形式提供下载。其中包含区分了公开/非公开的整套模块,以及分发到内部仓库的操作步骤。

下载示例代码(zip)

本文的示例代码,已经在 PowerShell 7.6 中实际运行验证过(Pester 16 项)。执行 zip 中包含的 Invoke-SampleTests.ps1,您也可以在自己的环境中重现同样的验证。

# 语法解析 + 静态分析 + Pester 测试
./Invoke-SampleTests.ps1

配置值(路径、服务器名、租户 ID 等)均为示例。请勿直接在生产环境中运行,请根据贵公司的环境进行相应替换。

相关文章

相关咨询领域

合同会社小村软件负责为企业内部脚本资产提供模块化改造、分发基础设施建设、人员依赖型运维的标准化,以及既有脚本可维护性改善等服务。

参考链接

  1. Microsoft Learn,Package management for PowerShell。关于 Microsoft.PowerShell.PSResourceGet 是替代 PowerShellGet 与 PackageManagement 的模块、随附于 PowerShell 7.4 并可与传统的 PowerShellGet 2.2.5 并存、Windows PowerShell 5.1 也可以从 PowerShell Gallery 引入等内容。  2 3 4 5

  2. Microsoft Learn,Register-PSResourceRepository。关于可以在 -Uri 中指定本地文件夹、文件共享(UNC 路径)或 NuGet 兼容源的 URL 来注册仓库、通过 -Trusted 进行信任设置、通过 -Priority 设定搜索顺序,以及在需要身份验证的仓库中指定凭据等内容。  2 3 4

  3. Microsoft Learn,How to write a PowerShell module manifest。关于用 New-ModuleManifest 创建清单、RootModule・ModuleVersion・GUID・PowerShellVersion・CompatiblePSEditions・RequiredModules 等各个键、FunctionsToExport 等导出指定不应使用通配符而应明确列出的原因(命令探索的性能)等内容。  2 3 4 5 6

  4. Microsoft Learn,Prerelease module versions。关于基于语义化版本管理的版本编号、通过 PrivateData.PSData.Prerelease 指定预发行版、预发行版不属于默认获取对象等内容。  2 3

  5. Microsoft Learn,Publish-PSResource。关于把 -Path 指定的模块文件夹发布到仓库、通过 -Repository 指定发布目标、通过 -ApiKey 进行身份验证等内容。 

  6. Microsoft Learn,Install-PSResource。关于通过 -Name / -Version / -Repository 进行安装、通过 -Scope(CurrentUser / AllUsers)指定放置位置、通过 -TrustRepository 省略确认等内容。另请参见通过 Update-PSResource 进行更新的相关内容。  2 3

  7. Microsoft Learn,about_PSModulePath。关于 PowerShell 会从 $env:PSModulePath 中列举的文件夹中搜索模块、Windows PowerShell 与 PowerShell 7 在用户作用域・全用户作用域的默认路径上有所不同等内容。  2 3 4 5

  8. Microsoft Learn,New-FileCatalog。关于可以生成包含文件夹下各文件哈希值的目录文件(.cat)、可以用 Set-AuthenticodeSignature 对目录进行签名、可以用 Test-FileCatalog 将目录与文件群进行比对以检测篡改等内容。执行策略验证的是执行对象脚本文件本身的 Authenticode 签名这一点,请参见 about_Execution_Policies(AllSigned 下只能执行由受信任发布者签名的脚本)以及 Set-AuthenticodeSignature(为文件添加 Authenticode 签名、通过 -TimestampServer 添加时间戳)。  2 3

  9. Microsoft Learn,New-SmbShare。关于可以通过 -FullAccess / -ChangeAccess / -ReadAccess 按账户分别指定共享级别的访问权限。NTFS 一侧的访问权限设置请参见 icacls(通过 /grant 授予权限、RX=读取与执行、M=更改、F=完全访问等权限掩码,(OI)=对象继承・(CI)=容器继承的指定)。/grant 的行为是”在已授予的显式权限基础上追加”,带 :r/grant:r 则会替换该主体的显式权限(其他主体的 ACE 无论哪种方式都会保留)。继承的切断请参见 ObjectSecurity.SetAccessRuleProtection(第一个参数用于保护继承,第二个参数用于指定是否继承已有的继承 ACE)。这个方法能够指定的只是继承 ACE 的处理方式,直接授予该对象的 ACE 不在其范围之内。要清除直接授予的权限,需要用 ObjectSecurity.RemoveAccessRuleSpecific 等方法逐条移除。  2 3

共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。

与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。

常见问题

汇总了咨询这一主题时常见的问题。

PowerShellGet 和 PSResourceGet 有什么区别?应该使用哪一个?
PSResourceGet(Microsoft.PowerShell.PSResourceGet)是替代传统 PowerShellGet 与 PackageManagement 的新一代模块管理机制,PowerShell 7.4 从一开始就内置了它。由于它可以与传统的 PowerShellGet 2.2.5 并存,因此可以在不破坏现有脚本的情况下完成迁移。如果是新写的内容,推荐使用命令名为 -PSResource 系列(Install-PSResource、Publish-PSResource 等)的 PSResourceGet。Windows PowerShell 5.1 也可以通过从 PowerShell Gallery 导入来使用它。
搭建内部仓库需要专用服务器吗?
不需要。最简单的方法是把文件共享上的文件夹注册为仓库,只需在 Register-PSResourceRepository 中指定 UNC 路径即可运作。既不需要专用服务器,也不需要数据库。如果作为组织需要发挥访问控制或审计的作用,或者需要从公司外部获取,则可以使用 Azure Artifacts、GitHub Packages 这类 NuGet 兼容源。现实的做法是先从文件共享开始,等出现需要时再迁移。
模块清单(.psd1)是必须的吗?
在实务中请将其视为必需项。虽然仅凭 .psm1 也能作为模块加载,但如果没有清单,就无法拥有版本号,也就无法知道「装的是哪个版本」。没有版本号,既无法管理更新,出现故障时也无法进行范围划分。此外,通过清单还可以明确要导出的函数、声明依赖模块、指定所支持的 PowerShell 版本和版本(Edition)。可以用 New-ModuleManifest 生成模板,创建成本也很低。
在 FunctionsToExport 中写 '*' 会有什么问题?
命令的自动探索会变慢,并且意料之外的内部函数也会被公开出去。PowerShell 在加载模块之前,需要知道哪个命令位于哪个模块中,如果使用通配符,就必须解析模块本体才能知道。用数组明确写出要公开的函数,就不需要这个解析过程了。另外,如果内部使用的辅助函数能够从外部调用,它实际上就变成了事实上的公开 API,之后就无法再更改了。
可以让使用方自动更新已分发的模块吗?
虽然可以用 Update-PSResource 进行更新,但对于业务脚本所依赖的模块,请谨慎设计其自动更新机制。因为这会导致无人值守执行的夜间批处理,在不知不觉中就用新版本运行了起来。在实务中,先在验证环境中确认新版本,再有计划地对生产环境进行更新,这样的运维方式更安全。如果无论如何都要自动化,请设置诸如固定主版本号进行更新(像 -Version '1.*' 这样指定范围)之类的限制措施。

作者简介

本文作者的个人简介页面。

Go Komura

小村软件有限公司 代表

以 Windows 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。

返回博客列表