在扩展中使用 PowerShell

我们来更深入地了解 Windows Admin Center 扩展 SDK - 我们将讨论向扩展中添加 PowerShell 命令。

TypeScript 中的 PowerShell

Gulp 生成过程有一个生成步骤,该步骤将获取放置在 \src\resources\scripts 文件夹中的任何 {!ScriptName}.ps1,并将它们生成到 \src\generated 文件夹下的 powershell-scripts 类中。

注意

不要手动更新 powershell-scripts.tsstrings.ts 文件。 所做的任何更改将在下一次生成时都被覆盖。

运行 PowerShell 脚本

要在节点上运行的任何脚本都可以放在 \src\resources\scripts\{!ScriptName}.ps1 中。

重要

在运行 gulp generate 之前,在 {!ScriptName}.ps1 文件中所做的任何更改都不会反映在你的项目中。

API 的工作方式是首先在目标节点上创建 PowerShell 会话,使用需要传入的任何参数创建 PowerShell 脚本,然后在创建的会话上运行脚本。

例如,我们有如下脚本:\src\resources\scripts\Get-NodeName.ps1

Param
 (
    [String] $stringFormat
 )
 $nodeName = [string]::Format($stringFormat,$env:COMPUTERNAME)
 Write-Output $nodeName

我们将为目标节点创建一个 PowerShell 会话:

const session = this.appContextService.powerShell.createSession('{!TargetNode}');

然后,我们将使用输入参数创建 PowerShell 脚本:

const command = PowerShell.createCommand(PowerShellScripts.Get_NodeName, {stringFormat: 'The name of the node is {0}!'});

最后,我们需要在创建的会话中运行该脚本:

  public ngOnInit(): void {
    this.session = this.appContextService.powerShell.createAutomaticSession('{!TargetNode}');
  }

  public getNodeName(): Observable<any> {
    const command = PowerShell.createCommand(PowerShellScripts.Get_NodeName, { stringFormat: 'The name of the node is {0}!'});
    return this.appContextService.powerShell.run(this.session, command)
    .pipe(
        map(
        response => {
            if (response && response.results) {
                return response.results;
            }
            return 'no response';
        }
      )
    );
  }

  public ngOnDestroy(): void {
    this.session.dispose()
  }

现在,我们需要订阅刚刚创建的 observable 函数。 将此函数放置在需要调用函数以运行 PowerShell 脚本的位置:

this.getNodeName().subscribe(
     response => {
	console.log(response)
     }
);

通过向 createSession 方法提供节点名称,将创建一个新的 PowerShell 会话并使用此会话,并在完成 PowerShell 调用后立即销毁此会话。

密钥选项

调用 PowerShell API 时,有几个选项可用。 每次创建会话时,都可以使用或不使用键来创建会话。

键:这将创建一个可以查找和重用的键控会话,甚至可以跨组件进行查找和重用(这意味着组件 1 可以使用键“SME-ROCKS”创建会话,而组件 2 可以使用相同的会话)。 如果提供了键,则必须通过调用 dispose() 来释放创建的会话,如上例中所示。 如果 5 分钟内没有处理会话,则应释放会话。

  const session = this.appContextService.powerShell.createSession('{!TargetNode}', '{!Key}');

无键:将自动为会话创建键。 此会话将在 3 分钟后自动处理。 使用无密钥允许你的扩展循环使用在创建会话时已经可用的任何运行空间。 如果没有可用的运行空间,则将创建一个新的运行空间。 此功能适用于一次性调用,但重复使用可能会影响性能。 创建一个会话大约需要 1 秒钟,因此持续回收会话可能会导致速度变慢。

  const session = this.appContextService.powerShell.createSession('{!TargetNodeName}');

or

const session = this.appContextService.powerShell.createAutomaticSession('{!TargetNodeName}');

在大多数情况下,在 ngOnInit() 方法中创建键控会话,之后在 ngOnDestroy() 中释放它。 如果组件中有多个 PowerShell 脚本,但基础会话不在组件之间共享,请遵循此模式。 为了获得最佳效果,请确保会话创建是在组件内部管理的,而不是在服务内部,这有助于确保可以正确管理生存期和清理。

为了获得最佳效果,请确保会话创建是在组件内部管理的,而不是在服务内部,这有助于确保可以正确管理生存期和清理。

PowerShell 流

如果你有一个很长的运行脚本,并且数据是逐步输出的,那么 PowerShell 流将允许你处理数据,而不必等待脚本完成。 收到数据后,将立即调用 observable next()。

this.appContextService.powerShellStream.run(session, script);

长时间运行的脚本

如果你希望在后台运行较长的运行脚本,则可以提交工作项。 网关将跟踪脚本的状态,并且可以将状态更新发送到通知。

const workItem: WorkItemSubmitRequest = {
	typeId: 'Long Running Script',
	objectName: 'My long running service',
	powerShellScript: script,

	//in progress notifications
	inProgressTitle: 'Executing long running request',
	startedMessage: 'The long running request has been started',
	progressMessage: 'Working on long running script – {{ percent }} %',

	//success notification
	successTitle: 'Successfully executed a long running script!',
	successMessage: '{{objectName}} was successful',
	successLinkText: 'Bing',
	successLink: 'http://www.bing.com',
	successLinkType: NotificationLinkType.Absolute,

	//error notification
	errorTitle: 'Failed to execute long running script',
	errorMessage: 'Error: {{ message }}'

	nodeRequestOptions: {
	   logAudit: true,
	   logTelemetry: true
	}
};

return this.appContextService.workItem.submit('{!TargetNode}', workItem);

注意

若要显示进度,在已编写的脚本中必须包含 Write-Progress。 例如:

 Write-Progress -Activity ‘The script is almost done!' -percentComplete 95

WorkItem 选项

函数 说明
submit() 提交工作项
submitAndWait() 提交工作项并等待其执行完成
wait() 等待现有工作项完成
query() 按 ID 查询现有工作项
find() 按 TargetNodeName、ModuleName 或 typeId 查找现有工作项。

PowerShell 批处理 API

如果需要在多个节点上运行相同的脚本,则可以使用批处理 PowerShell 会话。 例如:

const batchSession = this.appContextService.powerShell.createBatchSession(
	['{!TargetNode1}', '{!TargetNode2}', sessionKey);
  this.appContextService.powerShell.runBatchSingleCommand(batchSession, command).subscribe((responses: PowerShellBatchResponseItem[]) => {
	for (const response of responses) {
	  if (response.error || response.errors) {
	    //handle error
	  } else {
	    const results = response.properties && response.properties.results;
	    //response.nodeName
	    //results[0]
	  }
	}
     },
     Error => { /* handle error */ });

PowerShellBatch 选项

选项 说明
runSingleCommand 对阵列中的所有节点运行单个命令
run 在配对节点上运行相应的命令
cancel 取消阵列中所有节点上的命令