PowerShell 技能连载 - 添加参数自动完成(第 2 部分)

在上一个技能中,我们讨论了已添加到 PowerShell 7 新的 [ArgumentCompletions()] 属性,以及如何使用它向函数参数添加复杂的参数完成功能。

不幸的是,该属性在 Windows PowerShell 中不可用,因此使用此属性,您的代码将不再与 Windows PowerShell 兼容。

当然,您还可以将属性添加到 Windows PowerShell。当您在Windows PowerShell中运行以下代码时, [ArgumentCompletions()] 属性将变为可用。PowerShell 7 代码仍然保持兼容,现在您也可以在Windows PowerShell中开始使用此属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# are we running in Windows PowerShell?
if ($PSVersionTable.PSEdition -ne 'Core')
{
# add the attribute [ArgumentCompletions()]
$code = @'
using System;
using System.Collections.Generic;
using System.Management.Automation;

public class ArgumentCompletionsAttribute : ArgumentCompleterAttribute
{

private static ScriptBlock _createScriptBlock(params string[] completions)
{
string text = "\"" + string.Join("\",\"", completions) + "\"";
string code = "param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams);@(" + text + ") -like \"*$WordToComplete*\" | Foreach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }";
return ScriptBlock.Create(code);
}

public ArgumentCompletionsAttribute(params string[] completions) : base(_createScriptBlock(completions))
{
}
}
'@

$null = Add-Type -TypeDefinition $code *>&1
}

如您所见,代码仅在 Windows PowerShell 中运行时才添加新属性。在PowerShell 7中,该属性已内置。

现在,无论您打算在 Windows PowerShell 还是Windows 7 中使用它,都可以在函数中使用复杂的参数完成。只需将上面的代码添加到代码中,以确保存在该属性。

这是使用属性并提供参数完成的函数的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
function Get-Country
{
param
(
# suggest country names:
[ArgumentCompletions('USA','Germany','Norway','Sweden','Austria','YouNameIt')]
[string]
$Name
)

# return parameter
$PSBoundParameters
}

当您在 PowerShell ISE(它是纯粹的 Windows PowerShell)中运行代码,然后在交互式控制台中使用 Get-Country 时,PowerShell ISE 会自动为 -Name 参数建议参数值(国家/地区名称)。

这里还有两件事要考虑:

  • 由于 PowerShell 中存在长期的错误,这种类型的参数完成功能在定义实际功能的编辑器脚本窗格中不起作用。它始终可以在交互式控制台(这是最重要的用例)和任何其他脚本窗格中使用。
  • [ValidateSet()] 属性相反,新的 [ArgumentCompletions()] 属性并不将用户输入限制为列出的值。新属性仅提供您定义的建议,而不以任何方式限制用户输入。

有关此处使用的技术的更多详细信息,请访问 https://powershell.one/powershell-internals/attributes/auto-completion

PowerShell 技能连载 - 添加参数自动完成(第 1 部分)

在 PowerShell 函数参数中添加参数完成功能可以极大地提高函数的可用性。一种常见的方法是将 [ValidateSet()] 属性添加到您的参数中:

1
2
3
4
5
6
7
8
9
10
11
12
13
function Get-Country
{
param
(
# suggest country names
[ValidateSet('USA','Germany','Norway','Sweden','Austria','YouNameIt')]
[string]
$Name
)

# return parameter
$PSBoundParameters
}

现在,当用户使用 Get-Country 命令并传入 -Name 参数时,该函数现在会在用户按下 TAB 时建议国家/地区名称。像 PowerShell ISE 或 Visual Studio Code 这样的复杂PowerShell 编辑器甚至会自动打开 IntelliSense 菜单,或者在您按 CTRL + SPACE 显示所有的值。

但是,[ValidateSet()] 属性将用户限制为列出的值。无法输入其他值。如果只想为 -ComputerName 参数建议最常用的服务器名称,则将用户限制为仅使用这些服务器名称。这不是一个好主意。

从 PowerShell 7 开始,有一个名为 [ArgumentCompletions()] 的新属性,该属性几乎与 [ValidateSet()] 相似,但它跳过了验证部分。它提供相同的参数补全,但不限制用户输入:

1
2
3
4
5
6
7
8
9
10
11
12
13
function Get-Country
{
param
(
# suggest country names
[ArgumentCompletions('USA','Germany','Norway','Sweden','Austria','YouNameIt')]
[string]
$Name
)

# return parameter
$PSBoundParameters
}

此版本的 Get-Country 提供国家名称建议,但是如果您愿意,您仍然可以输入其他任何国家名称。

重要提示:由于 PowerShell 中的错误,参数自动完成对于定义具体功能的脚本窗格不起作用。而在 PowerShell 控制台和任何其他编辑器脚本窗格中,参数自动完成可以正常工作。

Windows PowerShell 中缺少新的 [ArgumentCompletions()] 属性,因此在使用它时,您的函数不再与 Windows PowerShell 兼容。我们将在即将到来的提示中解决此问题。

PowerShell 技能连载 - 解析 Windows 产品密钥(第 2 部分)

在上一个技能中,我们解释了如何向 WMI 请求 Windows 的部分产品密钥。如果您丢失了原始产品密钥,可以通过以下方法恢复完整密钥:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
function Get-WindowsProductKey{
\# test whether this is Windows 7 or older function Test-Win7 {
$OSVersion = [System.Environment]::OSVersion.Version ($OSVersion.Major -eq 6 -and $OSVersion.Minor -lt 2) -or $OSVersion.Major -le 6 }

\# implement decoder $code = @'// original implementation: https://github.com/mrpeardotnet/WinProdKeyFinder
using System;
using System.Collections;

public static class Decoder
{
public static string DecodeProductKeyWin7(byte[] digitalProductId)
{
const int keyStartIndex = 52;
const int keyEndIndex = keyStartIndex + 15;
var digits = new[]
{
'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'M', 'P', 'Q', 'R',
'T', 'V', 'W', 'X', 'Y', '2', '3', '4', '6', '7', '8', '9',
};
const int decodeLength = 29;
const int decodeStringLength = 15;
var decodedChars = new char[decodeLength];
var hexPid = new ArrayList();
for (var i = keyStartIndex; i <= keyEndIndex; i++)
{
hexPid.Add(digitalProductId[i]);
}
for (var i = decodeLength - 1; i >= 0; i--)
{
// Every sixth char is a separator.
if ((i + 1) % 6 == 0)
{
decodedChars[i] = '-';
}
else
{
// Do the actual decoding.
var digitMapIndex = 0;
for (var j = decodeStringLength - 1; j >= 0; j--)
{
var byteValue = (digitMapIndex << 8) | (byte)hexPid[j];
hexPid[j] = (byte)(byteValue / 24);
digitMapIndex = byteValue % 24;
decodedChars[i] = digits[digitMapIndex];
}
}
}
return new string(decodedChars);
}

public static string DecodeProductKey(byte[] digitalProductId)
{
var key = String.Empty;
const int keyOffset = 52;
var isWin8 = (byte)((digitalProductId[66] / 6) & 1);
digitalProductId[66] = (byte)((digitalProductId[66] & 0xf7) | (isWin8 & 2) * 4);

const string digits = "BCDFGHJKMPQRTVWXY2346789";
var last = 0;
for (var i = 24; i >= 0; i--)
{
var current = 0;
for (var j = 14; j >= 0; j--)
{
current = current*256;
current = digitalProductId[j + keyOffset] + current;
digitalProductId[j + keyOffset] = (byte)(current/24);
current = current%24;
last = current;
}
key = digits[current] + key;
}

var keypart1 = key.Substring(1, last);
var keypart2 = key.Substring(last + 1, key.Length - (last + 1));
key = keypart1 + "N" + keypart2;

for (var i = 5; i < key.Length; i += 6)
{
key = key.Insert(i, "-");
}

return key;
}
}'@ \# compile C# Add-Type -TypeDefinition $code
\# get raw product key $digitalId = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name DigitalProductId).DigitalProductId
$isWin7 = Test-Win7 if ($isWin7)
{
\# use static C# method [Decoder]::DecodeProductKeyWin7($digitalId)
}
else {
\# use static C# method: [Decoder]::DecodeProductKey($digitalId)
}
}

PowerShell 技能连载 - 解析 Windows 产品密钥(第 1 部分)

有很多脚本示例,甚至还有密钥恢复工具,它们都承诺会返回完整的产品密钥,但是在许多情况下,返回的密钥不是 Windows 产品密钥。

当您使用密钥恢复工具时,通常会丢失产品密钥,因此没有简单的方法来检查密钥恢复脚本或工具返回的承诺密钥是否正确。

幸运的是,WMI 至少可以返回“部分”产品密钥。这样,您可以验证恢复的密钥是否是有效的。

SoftwareLicensingProduct WMI 类返回有关大多数 Microsoft 产品的许可状态的详细信息。下面的此行获取所有以 “Windows” 开头且许可证状态为非 0 的 Microsoft 产品的所有许可信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
PS> Get-CimInstance -ClassName SoftwareLicensingProduct -Filter 'Name LIKE "Windows%" AND LicenseStatus>0'


ADActivationCsvlkPid :
ADActivationCsvlkSkuId :
ADActivationObjectDN :
ADActivationObjectName :
ApplicationID : 55c92734-d682-4d71-983e-d6ec3f16059f
AutomaticVMActivationHostDigitalPid2 :
AutomaticVMActivationHostMachineName :
AutomaticVMActivationLastActivationTime : 01.01.1601 01:00:00
Description : Windows(R) Operating System, OEM_DM channel
DiscoveredKeyManagementServiceMachineIpAddress :
DiscoveredKeyManagementServiceMachineName :
DiscoveredKeyManagementServiceMachinePort : 0
EvaluationEndDate : 01.01.1601 01:00:00
ExtendedGrace : 4294967295
GenuineStatus : 0
GracePeriodRemaining : 0
IAID :
ID : bd3762d7-270d-4760-8fb3-d829ca45278a
IsKeyManagementServiceMachine : 0
KeyManagementServiceCurrentCount : 4294967295
KeyManagementServiceFailedRequests : 4294967295
KeyManagementServiceLicensedRequests : 4294967295
KeyManagementServiceLookupDomain :
KeyManagementServiceMachine :
KeyManagementServiceNonGenuineGraceRequests : 4294967295
KeyManagementServiceNotificationRequests : 4294967295
KeyManagementServiceOOBGraceRequests : 4294967295
KeyManagementServiceOOTGraceRequests : 4294967295
KeyManagementServicePort : 0
KeyManagementServiceProductKeyID :
KeyManagementServiceTotalRequests : 4294967295
KeyManagementServiceUnlicensedRequests : 4294967295
LicenseDependsOn :
LicenseFamily : Professional
LicenseIsAddon : False
LicenseStatus : 1
LicenseStatusReason : 1074066433
MachineURL :
Name : Windows(R), Professional edition
OfflineInstallationId : 563276155667058052465840741114524545879016766601431504369777043
PartialProductKey : WFG6P
...

不幸的是,此调用需要很长时间才能完成。为了加快速度,请告诉 WMI 您要做什么,以便该调用不会计算您不需要的大量信息。下面的调用仅从所需实例中读取 PartialProductKey 属性,并且速度更快:

1
2
3
PS> Get-CimInstance -Query 'Select PartialProductKey From SoftwareLicensingProduct Where Name LIKE "Windows%" AND LicenseStatus>0' | Select-Object -ExpandProperty PartialProductKey

WFG6P

PowerShell 技能连载 - 读取操作系统详情

通过读取适当的注册表值,PowerShell 可以轻松检索重要的操作系统详细信息,例如内部版本号和版本:

1
2
3
4
# read operating system info
Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion' |
# pick selected properties
Select-Object -Property CurrentBuild,CurrentVersion,ProductId, ReleaseID, UBR

不过,其中一些值使用加密格式。例如,InstallTime 注册表项只是一个非常大的整数。

1
2
3
4
PS> $key = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
PS> (Get-ItemProperty -Path $key).InstallTime

132119809618946052

事实证明,这些是时间 tick 值,通过使用 [DateTime]类型及其 FromFileTime() 静态方法,您可以轻松地将时间 tick 值转换为有意义的安装日期:

1
2
3
4
5
6
PS> $key = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
PS> $ticks = (Get-ItemProperty -Path $key).InstallTime
PS> $date = [DateTime]::FromFileTime($ticks)
PS> "Your OS Install Date: $date"

Your OS Install Date: 09/03/2019 12:42:41

您可以在遇到时间 tick 值时使用 FromFileTime()。例如,Active Directory 也以这种格式存储日期。

PowerShell 技能连载 - 在控制台中提示输入凭据

当您运行 Get-Credential 或提示您输入用户名和密码时,Windows PowerShell (powershell.exe) 始终会打开一个单独的凭据对话框。而新的 PowerShell 7 (pwsh.exe) 则在控制台内提示:

1
2
3
4
5
6
7
8
9
10
11
PS> Get-Credential

PowerShell credential request
Enter your credentials.
User: Tobias
Password for user Tobias: ******


UserName Password
-------- --------
Tobias System.Security.SecureString

如果您更喜欢控制台提示而不是打开单独的对话框,则可以切换 Windows PowerShell 的默认行为。您需要管理员特权才能更改注册表设置:

1
2
$key = "HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds"
Set-ItemProperty -Path $key -Name ConsolePrompting -Value $true

若要恢复默认行为,请将值更改为 $false,或通过 Remove-ItemProperty 删除注册表值。

PowerShell 技能连载 - 在 PowerShell Gallery 搜索新模块

官方的 PowerShell Gallery 是一个公共仓库,其中包含数千个免费的 PowerShell 模块。无需重新设计轮子,而是完全可以浏览 gallery 以查找可重用的代码,这些代码可按原样使用或用作自己项目的起点。让我们看一下如何从 PowerShell 库中发现和下载 PowerShell 代码。

您可以在 https://powershellgallery.com 上使用其图形前端来搜索代码,但是 Find-Module cmdlet 是一种更好,更强大的方法。如果您正在寻找通过 PowerShell 管理 Office 365 的方法,可以通过下面这行代码获取包含 “Office” 关键字的所有模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Name                                                  CompanyName                               PublishedDate       Description
---- ----------- ------------- -----------
OfficeOnlineServerDsc {PowerShellTeam, gaelcolas, dsccommunity} 03.04.2020 22:01:30 The OfficeOnlineSe...
Office365DnsChecker rhymeswithmogul 30.03.2020 14:15:00 Checks a domain's ...
Microsoft.Graph.DevicesApps.OfficeConfiguration msgraph-sdk-powershell 17.03.2020 01:24:39 Microsoft Graph Po...
IntraOffice.ContentRepository.Powershell rderegt 06.03.2020 14:26:35 Client library for...
Office365DSC NikCharleboisPFE 04.03.2020 23:13:30 This DSC module is...
Office365PowershellUtils joshn-whatcomtrans.net 03.03.2020 00:26:59 A collection of cm...
Office365Cmdlets CData 20.02.2020 20:13:29 CData Cmdlets for ...
MSPOffice365Tools majorwitteman 13.02.2020 20:26:15 Collection of Offi...
AdminToolbox.Office365 {TaylorLee, Taylor_Lee} 27.01.2020 15:26:36 Functions for work...
OfficeAddinManager DarrenDK 17.12.2019 07:10:08 Module for managin...
PSP-Office365 powershellpr0mpt 20.11.2019 10:57:08 Helper module to g...
Office365MailAliases Cloudenius 17.11.2019 11:57:07 This module contai...
Office365Toolkit PatrickJD84 03.09.2019 03:01:36 A collection of sc...
Office365.Connect nicomartens 22.08.2019 07:58:43 Uses the Windows C...
Office365TokenGet junecastillote 17.07.2019 03:21:07 Helps you acquire ...
BitTitan.Runbooks.Office365SecurityAndCompliance.Beta BT_AutomationEngineers 14.05.2019 08:41:04 PowerShell module ...
BitTitan.Runbooks.Office365SecurityAndCompliance BT_AutomationEngineers 12.03.2019 07:22:10 PowerShell module ...
Office365Module Giertz 24.01.2019 22:56:08 test for ez
ZIM.Office365 Mikezim 14.12.2018 11:53:54 Provides a set of ...
MZN.Office365 michael.zimmerman 14.12.2018 08:10:26 Provides a set of ...
JumpCloud.Office365.SSO Scottd3v 14.06.2018 16:13:13 Functions to enabl...
Office365GraphAPI chenxizhang 12.06.2017 15:14:57 Office 365 Graph A...
Office365Connect Gonjer 18.05.2017 21:13:41 Office365Connect i...
RackspaceCloudOffice {mlk, paul.trampert.rackspace} 28.09.2016 14:34:25 REST client for th...
Office365 StevenAyers 16.07.2016 10:53:36 For Microsoft Part...
OfficeProvider abaker 01.03.2016 21:00:35 OfficeProvider all...

该列表包括发布者和模块描述,并按从新到旧的顺序对模块进行排序。PublishedDate 列指示模块是否是最近刚添加到 gallery中,这样您可以立即查看它是否维护良好并且值得一看。

如果您发现某个特定模块有趣,请获取其所有元数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
PS> Find-Module -Name Office365PowershellUtils -Repository PSGallery | Select-Object -Property *


Name : Office365PowershellUtils
Version : 1.1.5
Type : Module
Description : A collection of cmdlets for managing Office365
Author : R. Josh Nylander
CompanyName : joshn-whatcomtrans.net
Copyright : (c) 2012 WTA. All rights reserved.
PublishedDate : 03.03.2020 00:26:59
InstalledDate :
UpdatedDate :
LicenseUri :
ProjectUri :
IconUri :
Tags : {PSModule}
Includes : {Function, RoleCapability, Command, DscResource...}
PowerShellGetFormatVersion :
ReleaseNotes :
Dependencies : {}
RepositorySourceLocation : https://www.powershellgallery.com/api/v2
Repository : PSGallery
PackageManagementProvider : NuGet
AdditionalMetadata : @{summary=A collection of cmdlets for managing Office365; versionDownloadCount=33; ItemType=Module;
copyright=(c) 2012 WTA. All rights reserved.; PackageManagementProvider=NuGet; CompanyName=Whatcom
Transportation Authority; SourceName=PSGallery; tags=PSModule; created=03.03.2020 00:26:59 +01:00;
description=A collection of cmdlets for managing Office365; published=03.03.2020 00:26:59 +01:00;
developmentDependency=False; NormalizedVersion=1.1.5; downloadCount=296;
GUID=c6b26555-2b5f-45bc-affe-ef1c31580df3; lastUpdated=02.04.2020 16:50:22 +02:00; Authors=R. Josh
Nylander; updated=2020-04-02T16:50:22Z; Functions=Find-MsolUsersWithLicense
Update-MsolLicensedUsersFromGroup Update-MsolUserUsageLocation Change-ProxyAddress Add-ProxyAddress
Remove-ProxyAddress Set-ProxyAddress Sync-ProxyAddress Test-ProxyAddress Get-ProxyAddressDefault
Enable-SecurityGroupAsDistributionGroup Disable-SecurityGroupAsDistributionGroup Start-DirSync
Get-NextDirSync Suspend-UserMailbox Resume-UserMailbox Test-Mailbox Get-MailboxMemberOf
Clear-MailboxMemberOf Use-Office365 Export-PSCredential Import-PSCredential; isLatestVersion=True;
PowerShellVersion=3.0; IsPrerelease=false; isAbsoluteLatestVersion=True; packageSize=16635; FileList=Office3
65PowershellUtils.nuspec|Function_Connect-Office365.ps1|Office365PowershellUtils.psd1|Office365PowerShellUti
ls_mod.psm1|PSCredentials.psm1|README|SampleMigrationScripts\Monitor-MoveStats.ps1|SampleMigrationScripts\Re
sume-FirstFiveSuspended.ps1|SampleMigrationScripts\Set-MailboxTimeZone.ps1|SampleMigrationScripts\Set-Remote
RoutingAddress.ps1|SampleMigrationScripts\Set-RetentionPolicy.ps1|SampleMigrationScripts\Set-RoleAssignmentP
olicy.ps1; requireLicenseAcceptance=False}

如果您只对源代码感兴趣,请使用 Save-Module 并将模块下载到您选择的文件夹中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# path to source code
$path = "c:\sources"

# name of module to investigate
$moduleName = "Office365PowershellUtils"

# create folder
$null = New-Item -Path $path -ItemType Directory

# download module
Save-Module -Name $moduleName -Path $path -Repository PSGallery

# open folder with sources
explorer (Join-Path -Path $path -ChildPath $moduleName)

如果您想按原样实际使用该模块,请改用 Install-Module

1
PS> Install-Module -Name Office365PowershellUtils -Scope CurrentUser -Repository PSGallery

PowerShell 技能连载 - 管理 SharePoint Online

如果您使用 SharePoint Online,并希望通过 PowerShell 对其进行管理,请从 PowerShell Gallery 中下载并安装 Microsoft.Online.SharePoint.PowerShell 模块:

1
2
3
4
5
6
7
8
9
# search for the module in PowerShell Gallery (optional)
PS> Find-Module -Name Microsoft.Online.SharePoint.PowerShell

Version Name Repository Description
------- ---- ---------- --------
16.0.19927.12000 Microsoft.Online.SharePoint.Powe... PSGallery Microsoft SharePoint Online

# install the module in your personal scope (no admin privileges required)
PS> Install-Module -Name Microsoft.Online.SharePoint.PowerShell -Repository PSGallery -Scope CurrentUser

现在,您可以使用大量新的 PowerShell cmdlet 来管理 SharePoint Online:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
PS> Get-Command -Module Microsoft.Online.SharePoint.PowerShell | Format-Wide -Column 3


Add-SPOGeoAdministrator Add-SPOHubSiteAssociation Add-SPOHubToHubAssocia...
Add-SPOOrgAssetsLibrary Add-SPOSiteCollectionAp... Add-SPOSiteDesign
Add-SPOSiteDesignTask Add-SPOSiteScript Add-SPOSiteScriptPackage
Add-SPOTenantCdnOrigin Add-SPOTheme Add-SPOUser
Approve-SPOTenantServic... Approve-SPOTenantServic... Connect-SPOService
ConvertTo-SPOMigrationE... ConvertTo-SPOMigrationT... Deny-SPOTenantServiceP...
Disable-SPOTenantServic... Disconnect-SPOService Enable-SPOCommSite
Enable-SPOTenantService... Export-SPOQueryLogs Export-SPOUserInfo
Export-SPOUserProfile Get-SPOAppErrors Get-SPOAppInfo
Get-SPOBrowserIdleSignOut Get-SPOBuiltInDesignPac... Get-SPOCrossGeoMovedUsers
Get-SPOCrossGeoMoveReport Get-SPOCrossGeoUsers Get-SPODataEncryptionP...
Get-SPODeletedSite Get-SPOExternalUser Get-SPOGeoAdministrator
Get-SPOGeoMoveCrossComp... Get-SPOGeoStorageQuota Get-SPOHideDefaultThemes
Get-SPOHomeSite Get-SPOHubSite Get-SPOKnowledgeHubSite
Get-SPOMigrationJobProg... Get-SPOMigrationJobStatus Get-SPOMultiGeoCompany...
Get-SPOMultiGeoExperience Get-SPOOrgAssetsLibrary Get-SPOOrgNewsSite
Get-SPOPublicCdnOrigins Get-SPOSite Get-SPOSiteCollectionA...
Get-SPOSiteContentMoveS... Get-SPOSiteDataEncrypti... Get-SPOSiteDesign
Get-SPOSiteDesignRights Get-SPOSiteDesignRun Get-SPOSiteDesignRunSt...
Get-SPOSiteDesignTask Get-SPOSiteGroup Get-SPOSiteRenameState
Get-SPOSiteScript Get-SPOSiteScriptFromList Get-SPOSiteScriptFromWeb
Get-SPOSiteUserInvitations Get-SPOStorageEntity Get-SPOStructuralNavig...
Get-SPOStructuralNaviga... Get-SPOTenant Get-SPOTenantCdnEnabled
Get-SPOTenantCdnOrigins Get-SPOTenantCdnPolicies Get-SPOTenantContentTy...
Get-SPOTenantLogEntry Get-SPOTenantLogLastAva... Get-SPOTenantOrgRelation
Get-SPOTenantOrgRelatio... Get-SPOTenantOrgRelatio... Get-SPOTenantServicePr...
Get-SPOTenantServicePri... Get-SPOTenantSyncClient... Get-SPOTenantTaxonomyR...
Get-SPOTheme Get-SPOUnifiedGroup Get-SPOUnifiedGroupMov...
Get-SPOUser Get-SPOUserAndContentMo... Get-SPOUserOneDriveLoc...
Get-SPOWebTemplate Grant-SPOHubSiteRights Grant-SPOSiteDesignRights
Invoke-SPOMigrationEncr... Invoke-SPOSiteDesign Invoke-SPOSiteSwap
New-SPOMigrationEncrypt... New-SPOMigrationPackage New-SPOPublicCdnOrigin
New-SPOSdnProvider New-SPOSite New-SPOSiteGroup
New-SPOTenantOrgRelation Register-SPODataEncrypt... Register-SPOHubSite
Remove-SPODeletedSite Remove-SPOExternalUser Remove-SPOGeoAdministr...
Remove-SPOHomeSite Remove-SPOHubSiteAssoci... Remove-SPOHubToHubAsso...
Remove-SPOKnowledgeHubSite Remove-SPOMigrationJob Remove-SPOMultiGeoComp...
Remove-SPOOrgAssetsLibrary Remove-SPOOrgNewsSite Remove-SPOPublicCdnOrigin
Remove-SPOSdnProvider Remove-SPOSite Remove-SPOSiteCollecti...
Remove-SPOSiteCollectio... Remove-SPOSiteDesign Remove-SPOSiteDesignTask
Remove-SPOSiteGroup Remove-SPOSiteScript Remove-SPOSiteUserInvi...
Remove-SPOStorageEntity Remove-SPOTenantCdnOrigin Remove-SPOTenantOrgRel...
Remove-SPOTenantSyncCli... Remove-SPOTheme Remove-SPOUser
Remove-SPOUserInfo Remove-SPOUserProfile Repair-SPOSite
Request-SPOPersonalSite Request-SPOUpgradeEvalu... Restore-SPODataEncrypt...
Restore-SPODeletedSite Revoke-SPOHubSiteRights Revoke-SPOSiteDesignRi...
Revoke-SPOTenantService... Revoke-SPOUserSession Set-SPOBrowserIdleSignOut
Set-SPOBuiltInDesignPac... Set-SPOGeoStorageQuota Set-SPOHideDefaultThemes
Set-SPOHomeSite Set-SPOHubSite Set-SPOKnowledgeHubSite
Set-SPOMigrationPackage... Set-SPOMultiGeoCompanyA... Set-SPOMultiGeoExperience
Set-SPOOrgAssetsLibrary Set-SPOOrgNewsSite Set-SPOSite
Set-SPOSiteDesign Set-SPOSiteGroup Set-SPOSiteOffice365Group
Set-SPOSiteScript Set-SPOSiteScriptPackage Set-SPOStorageEntity
Set-SPOStructuralNaviga... Set-SPOStructuralNaviga... Set-SPOTenant
Set-SPOTenantCdnEnabled Set-SPOTenantCdnPolicy Set-SPOTenantContentTy...
Set-SPOTenantSyncClient... Set-SPOTenantTaxonomyRe... Set-SPOUnifiedGroup
Set-SPOUser Set-SPOWebTheme Start-SPOSiteContentMove
Start-SPOSiteRename Start-SPOUnifiedGroupMove Start-SPOUserAndConten...
Stop-SPOSiteContentMove Stop-SPOUserAndContentMove Submit-SPOMigrationJob
Test-SPOSite Unregister-SPOHubSite Update-SPODataEncrypti...
Update-UserType Upgrade-SPOSite Verify-SPOTenantOrgRel...

第一步总是从 Connect-SPOService 开始,连接到SharePoint Online:

1
Get-Help -Name Connect-SPOService -ShowWindow

接下来,使用查找动词为 Get 的 cmdlet这将安全地提供大量信息,但不会更改任何设置,也不会损坏任何东西:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
PS> Get-Command -Verb Get -Module Microsoft.Online.SharePoint.PowerShell | Format-Wide -Column 3


Get-SPOAppErrors Get-SPOAppInfo Get-SPOBrowserIdleSignOut
Get-SPOBuiltInDesignPack... Get-SPOCrossGeoMovedUsers Get-SPOCrossGeoMoveReport
Get-SPOCrossGeoUsers Get-SPODataEncryptionPolicy Get-SPODeletedSite
Get-SPOExternalUser Get-SPOGeoAdministrator Get-SPOGeoMoveCrossComp...
Get-SPOGeoStorageQuota Get-SPOHideDefaultThemes Get-SPOHomeSite
Get-SPOHubSite Get-SPOKnowledgeHubSite Get-SPOMigrationJobProg...
Get-SPOMigrationJobStatus Get-SPOMultiGeoCompanyAl... Get-SPOMultiGeoExperience
Get-SPOOrgAssetsLibrary Get-SPOOrgNewsSite Get-SPOPublicCdnOrigins
Get-SPOSite Get-SPOSiteCollectionApp... Get-SPOSiteContentMoveS...
Get-SPOSiteDataEncryptio... Get-SPOSiteDesign Get-SPOSiteDesignRights
Get-SPOSiteDesignRun Get-SPOSiteDesignRunStatus Get-SPOSiteDesignTask
Get-SPOSiteGroup Get-SPOSiteRenameState Get-SPOSiteScript
Get-SPOSiteScriptFromList Get-SPOSiteScriptFromWeb Get-SPOSiteUserInvitations
Get-SPOStorageEntity Get-SPOStructuralNavigat... Get-SPOStructuralNaviga...
Get-SPOTenant Get-SPOTenantCdnEnabled Get-SPOTenantCdnOrigins
Get-SPOTenantCdnPolicies Get-SPOTenantContentType... Get-SPOTenantLogEntry
Get-SPOTenantLogLastAvai... Get-SPOTenantOrgRelation Get-SPOTenantOrgRelatio...
Get-SPOTenantOrgRelation... Get-SPOTenantServicePrin... Get-SPOTenantServicePri...
Get-SPOTenantSyncClientR... Get-SPOTenantTaxonomyRep... Get-SPOTheme
Get-SPOUnifiedGroup Get-SPOUnifiedGroupMoveS... Get-SPOUser
Get-SPOUserAndContentMov... Get-SPOUserOneDriveLocation Get-SPOWebTemplate

当您适应了以后,可以接着查看更改和管理 SharePoint 的其余 cmdlet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
PS> Get-Command -Module Microsoft.Online.SharePoint.PowerShell | Group-Object Verb -NoElement | Sort-Object Count -Desc

Count Name
----- ----
63 Get
30 Set
29 Remove
12 Add
7 New
4 Start
4 Revoke
3 Export
3 Invoke
2 Update
2 Stop
2 Restore
2 Request
2 Register
2 Grant
2 Enable
2 ConvertTo
2 Approve
1 Repair
1 Disconnect
1 Disable
1 Deny
1 Connect
1 Submit
1 Test
1 Unregister
1 Upgrade
1 Verify

PowerShell 技能连载 - 检测泄露的密码(第 2 部分)

当您想向 PowerShell 函数提交敏感信息时,通常使用 SecureString 类型。这种类型可确保用户通过一个带遮罩的对话框输入数据,这样能保护输入内容免受不会被旁人看到。

由于 SecureString 始终可以由创建 SecureString 的人解密为纯文本,因此您可以利用带的输入框,但仍可以使用输入的纯文本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Test-Password
{
[CmdletBinding()]
param
(
[Parameter(Mandatory, Position=0)]
[System.Security.SecureString]
$Password
)

# take a SecureString and get the entered plain text password
# we are using a SecureString only to get a masked input box
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)
$plain = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)

"You entered: $plain"
}

当您运行代码然后运行 Test-Password 时,系统会提示您带有遮罩的输入。在函数内部,会将提交的 SecureString 解密为纯文本。

但是,这种方法有一个明显的缺点:如果希望通过参数传入信息,则现在必须提交 SecureString。您不能再传入纯文本:

1
2
3
4
5
6
7
# fails:
PS> Test-Password -Password test
Test-Password : Cannot process argument transformation on parameter 'Password'. Cannot convert the "test" value of type "System.String" to type "System.Security.SecureString".

# works
PS> Test-Password -Password ("test" | ConvertTo-SecureString -AsPlainText -Force)
You entered: test

不过,使用自定义属性,您可以为任何参数添加自动功能,以将纯文本自动转换为 SecureString:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# create a transform attribute that transforms plain text to a SecureString
class SecureStringTransformAttribute : System.Management.Automation.ArgumentTransformationAttribute
{
[object] Transform([System.Management.Automation.EngineIntrinsics]$engineIntrinsics, [object] $inputData)
{ if ($inputData -is [SecureString]) { return $inputData }
elseif ($inputData -is [string]) { return $inputData | ConvertTo-SecureString -AsPlainText -Force }
throw "Unexpected Error."
}
}

function Test-Password
{
[CmdletBinding()]
param
(
[Parameter(Mandatory, Position=0)]
[System.Security.SecureString]
[SecureStringTransform()]
$Password
)

# take a SecureString and get the entered plain text password
# we are using a SecureString only to get a masked input box
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)
$plain = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)

"You entered: $plain"
}

现在,用户可以在不使用参数的情况下运行 Test-Password,并获得带掩码对话框的提示。用户还可以直接传入纯文本:

1
2
3
4
5
6
7
8
9
10
# use built-in masked input
PS> Test-Password
cmdlet Test-Password at command pipeline position 1
Supply values for the following parameters:
Password: ******
You entered: secret

# use text-to-SecureString transformation attribute
PS> Test-Password -Password secret
You entered: secret

如果您想了解转换属性的工作原理,请查看以下详细信息:https://powershell.one/powershell-internals/attributes/transformation

PowerShell 技能连载 - 检测泄露的密码(第 1 部分)

密码复杂时也不一定是安全的。相反,您需要确保密码没有受到破坏,并且不在默认的攻击者词典中。如果自动攻击经常检查该密码,那么即使是最复杂的密码也不安全。

要确定密码是否被泄露,请使用以下功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function Test-Password
{
[CmdletBinding()]
param
(
[Parameter(Mandatory, Position=0)]
[System.Security.SecureString]
$Password
)

# take securestring and get the entered plain text password
# we are using a securestring only to get a masked input box
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)
$plain = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)

# hash the password
$bytes = [Text.Encoding]::UTF8.GetBytes($plain)
$stream = [IO.MemoryStream]::new($bytes)
$hash = Get-FileHash -Algorithm 'SHA1' -InputStream $stream
$stream.Close()
$stream.Dispose()

# separate the first 5 hash characters from the rest
$first5hashChars,$remainingHashChars = $hash.Hash -split '(?<=^.{5})'

# send the first 5 hash characters to the web service
$url = "https://api.pwnedpasswords.com/range/$first5hashChars"
[Net.ServicePointManager]::SecurityProtocol = 'Tls12'
$response = Invoke-RestMethod -Uri $url -UseBasicParsing

# split result into individual lines...
$lines = $response -split '\r\n'
# ...and get the line where the returned hash matches your
# remainder of the hash that you kept private
$filteredLines = $lines -like "$remainingHashChars*"

# return the number of compromises
[int]($filteredLines -split ':')[-1]
}

使用起来非常简单:只需将密码传给 Test-Password 函数即可。它返回已知泄露的数量,并且返回大于 0 泄露的任何密码都被认为是不安全的,必须进行更改。

1
2
3
4
PS> $password = Read-Host -AsSecureString

PS> Test-Password -Password $password
4880

密码必须作为 SecureString 提交。您可以不带密码运行 Test-Password,在这种情况下,系统会提示您。或者您需要以 SecureString 形式读取密码。

在该示例中,复杂密码 “P@$$w0rd” 在 4880 次攻击中被泄露,使用起来非常不安全。