讓我們深入了解 Windows Admin Center 延伸模組 SDK - 讓我們來討論如何將 PowerShell 命令新增至延伸模組。
TypeScript 中的 PowerShell
gulp 建置程序包含一個產生步驟,其會採用在 {!ScriptName}.ps1 資料夾中放置的任何 \src\resources\scripts,並將其建置為 powershell-scripts 資料夾下的 \src\generated 類別。
Note
請勿手動更新 powershell-scripts.ts 或 strings.ts 檔案。 將在下一次產生時覆寫您所做的任何變更。
正在執行 PowerShell 指令碼
您想要在節點上執行的任何指令碼都可以放在 \src\resources\scripts\{!ScriptName}.ps1 中。
Important
在執行 {!ScriptName}.ps1 之前,gulp generate 檔案中的任何變更都不會在專案中反映。
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()
}
現在,我們需要訂閱我們剛建立的可觀察函式。 請將此放在您需要呼叫此函式以執行 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 資料流將可讓您處理資料,而不需要等候指令碼完成。 一收到資料,就會立即呼叫可觀察的 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);
Note
若要顯示進度,必須在您已撰寫的指令碼中包含寫入進度。 例如:
Write-Progress -Activity ‘The script is almost done!' -percentComplete 95
WorkItem 選項
| 函式 | Explanation |
|---|---|
| submit() | 提交工作項目 |
| submitAndWait() | 提交工作項目,並等候其執行完成 |
| wait() | 等候現有的工作項目完成 |
| query() | 依照 ID 查詢既有的工作項目 |
| find() | 依 TargetNodeName、ModuleName 或 typeId 尋找現有的工作項目。 |
PowerShell Batch 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 選項
| option | Explanation |
|---|---|
| runSingleCommand | 對陣列中的所有節點執行單一命令 |
| 執行 | 對配對的節點執行對應的命令 |
| cancel | 取消陣列中所有節點上的命令 |