أمر PowerShell Invoke-Command: دليل شامل

المحترفون في تكنولوجيا المعلومات نادراً ما يعملون فقط على الكمبيوتر المحلي الخاص بنا. باستخدام أمر PowerShell Invoke-Command، ليس علينا ذلك! يسمح لنا هذا الأمر بكتابة الشيفرة بسلاسة كما لو كنا نعمل على الكمبيوتر المحلي الخاص بنا.

عن طريق استخدام ميزة PowerShell Remoting، يعتبر أمر Invoke-Command هو أمر PowerShell الذي يستخدم على نطاق واسع والذي يتيح للمستخدم تنفيذ شيفرة داخل PSSession. يمكن أن يكون هذا الجلسة إما واحدة تم إنشاؤها مسبقًا باستخدام أمر New-PSSession أو يمكنه بسرعة إنشاء جلسة مؤقتة وإلغاءها أيضًا.

ذات صلة: PowerShell Remoting: الدليل النهائي

فكر في Invoke-Command كـ PowerShell psexec. على الرغم من تنفيذهم بطرق مختلفة، إلا أن المفهوم هو نفسه. خذ شيفرة أو أمرًا صغيرًا وقم بتشغيله “محليًا” على الكمبيوتر عن بعد.

لكن يجب أن يكون لديك تمكين PowerShell Remoting وتوفره على الكمبيوتر عن بعد لكي يعمل Invoke-Command. عند الافتراض، يتم تمكينه بشكل افتراضي على جميع أجهزة Windows Server 2012 R2 أو الأحدث مع استثناءات جدار الحماية المناسبة. إذا كنت غير محظوظ بما فيه الكفاية للاستمرار في استخدام أجهزة Server 2008، هناك طرق متعددة لإعداد Remoting ولكن أحد الطرق السهلة هو تشغيل winrm quickconfig أو Enable-PSRemoting على الكمبيوتر عن بعد.

لتوضيح كيف يعمل الأمر Invoke-Command مع “أمر عشوائي” أي أمر لا يتطلب إنشاء جلسة جديدة لـ PSSession ، دعنا نفترض أن لديك جهاز كمبيوتر متصل بالمجال عن بعد يعمل بنظام Windows Server 2012 R2 أو الإصدارات الأحدث. الأمور تصبح معقدة قليلاً عند العمل على أجهزة الكمبيوتر في مجموعة العمل. سأفتح نافذة وحدة التحكم PowerShell الخاصة بي وأكتب امر Invoke-Command وأضغط على زر Enter.

PS> Invoke-Command
cmdlet Invoke-Command at command pipeline position 1
Supply values for the following parameters:
ScriptBlock:

I’m immediately asked to provide a scriptblock. The scriptblock is the code that we’re going to run on the remote computer.

حتى نتمكن من إثبات أن الكود داخل سكريبت بلوك يتم تنفيذه على الكمبيوتر عن بعد ، لنقم فقط بتشغيل أمر hostname. هذا الأمر سيعيد اسم الكمبيوتر الذي يعمل عليه. تشغيل امر hostname على جهازي المحلي يعيد اسمه.

PS> hostname
MACWINVM

لنمرر الآن سكريبت بلوك بهذا الكود نفسه داخل سكريبت بلوك إلى Invoke-Command. قبل أن نفعل ذلك ، ننسى معلمة مطلوبة: اسم الكمبيوتر. يجب علينا أن نخبر Invoke-Command بأي كمبيوتر عن بعد لتشغيل هذا الأمر عليه.

PS> Invoke-Command -ScriptBlock { hostname } -ComputerName WEBSRV1 WEBSRV1

لاحظ أن ناتج امر hostname الآن هو اسم الكمبيوتر البعيد WEBSRV1. لقد قمت بتشغيل بعض الشفرة على WEBSRV1. تشغيل الشفرة البسيطة داخل سكريبت بلوك وتمريرها إلى آلة بعيدة واحدة هو أبسط تطبيق لأمر Invoke-Command ولكنه يمكن أن يفعل الكثير أكثر من ذلك بكثير.

تمرير المتغيرات المحلية إلى سكريبتات الأمر عن بُعد.

لن تجد مرجع واحد لـ Invoke-Command داخل نص ما. عادةً، سيكون نصك طويلًا بعشرات الأسطر، مع تحديد متغيرات في أماكن معينة، وتعريف وظائف في وحدات وما إلى ذلك. على الرغم من أن تضمين بعض الشفرة داخل قوسين معقوفين قد يبدو بريئًا، إلا أنك في الواقع تغير نطاق الشفرة بأكملها التي تعمل فيه. بعد كل شيء، أنت ترسل تلك الشفرة إلى جهاز كمبيوتر بعيد. هذا الكمبيوتر البعيد ليس لديه فكرة عن كل الشفرة المحلية على جهازك إلا ما هو موجود في كتلة النص.

على سبيل المثال، ربما لديك وظيفة تحتوي على اسم الكمبيوتر ومعلمة لمسار الملف. هدف هذه الوظيفة هو تشغيل برنامج تثبيت البرمجيات على الكمبيوتر البعيد. يمكنك تمرير اسم الكمبيوتر ومسار الملف “المحلي” إلى برنامج التثبيت الموجود بالفعل على الكمبيوتر البعيد.

يبدو أن الوظيفة أدناه معقولة، أليس كذلك؟ لنقم بتشغيلها.

function Install-Stuff {
    param(
    	[Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$ComputerName,
        
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$InstallerFilePath
	)
    Invoke-Command -ComputerName $ComputerName -ScriptBlock { & $InstallerFilePath }
}

PS> Install-Stuff -ComputerName websrv1 -InstallerFilePath 'C:\install.exe'
The expression after '&' in a pipeline element produced an object that was not valid. It must result in a command name, a script block, or a CommandInfo object.
+ CategoryInfo          : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : BadExpression
+ PSComputerName        : websrv1

تفشل برسالة خطأ غامضة بسبب استخدامي لعامل الشيفرة. لم يكن الكود خطأ ولكن فشل بسبب أن $InstallerFilePath كانت فارغة على الرغم من أنك قمت بتمرير قيمة مع المعلمة الدالة. يمكننا اختبار هذا بتبديل علامة الـ ampersand مع Write-Host.

سطر الوظيفة الجديد: Invoke-Command -ComputerName $ComputerName -ScriptBlock { Write-Host "مسار المثبت هو: $InstallerFilePath" }

PS> Install-Stuff -ComputerName websrv1 -InstallerFilePath 'C:\install.exe'
Installer path is:
PS>

لاحظ أن قيمة $InstallerFilePath لا شيء. لم يتم توسيع المتغير لأنه لم يتم تمريره إلى الجهاز البعيد. لتمرير المتغيرات المحددة محليًا إلى السكريبت بلوك البعيد، لدينا خياران؛ يمكننا أن نضيف اسم المتغير مسبوقًا بـ $using: داخل السكريبت بلوك، أو يمكننا استخدام Invoke-Command مع معلمة ArgumentList. دعنا نلقي نظرة على كل منهما.

معلمة ArgumentList

طريقة واحدة لتمرير المتغيرات المحلية إلى سكريبت بلوك بعيد هي استخدام معلمة ArgumentList في Invoke-Command. تسمح هذه المعلمة لك بتمرير المتغيرات المحلية إلى المعلمة واستبدال مراجع المتغير المحلية في سكريبت بلوك بمكان علامات الاستبدال.

تمرير المتغيرات المحلية إلى معلمة ArgumentList سهل.

Invoke-Command -ComputerName WEBSRV1 -ScriptBlock { & $InstallerFilePath } -ArgumentList $InstallerFilePath

الجزء الذي يشوش على بعض الناس هو كيفية بناء المتغيرات داخل سكريبت بلوك. بدلاً من استخدام { & $InstallerPath }، نحتاج إلى تغييره إلى { & $args[0] } أو {param($foo) & $foo }. كلا الطريقتين تعمل بنفس الطريقة لكن أي واحدة يجب عليك استخدامها؟

معلمة ArgumentList هي مجموعة كائنات. تسمح مجموعات الكائنات لك بتمرير كائن أو أكثر في كل مرة. في هذا الحال، أنا فقط أمرر واحد.

عند التنفيذ ، يأخذ cmdlet Invoke-Command هذه المجموعة ثم يحقنها في scriptblock مما يحولها أساسًا إلى مصفوفة تسمى $args. تذكر أن $args -eq ArgumentList. في هذه النقطة ، ستشير إلى كل فهرس في المجموعة تمامًا كما تفعل مع مصفوفة. في حالتنا ، كان لدينا عنصر واحد فقط في المجموعة ($InstallerFilePath) الذي “ترجم” إلى $args[0] مما يعني الفهرس الأول في تلك المجموعة. ومع ذلك ، إذا كان لديك المزيد ، يمكنك الإشارة إليها بـ $args[1] ، $args[2] وهكذا.

بالإضافة إلى ذلك ، إذا كنت تفضل تعيين أسماء متغيرة أفضل لمتغيرات scriptblock ، يمكنك أيضًا إضافة معلمات إلى الـ scriptblock تمامًا كما تفعل مع وظيفة. بعد كل شيء ، الـ scriptblock هو فقط وظيفة مجهولة. لإنشاء معلمات scriptblock ، قم بإنشاء كتلة param بـ اسم المعلمة. بمجرد الإنشاء ، ثم اشير إلى تلك المعلمة في scriptblock كما هو موضح أدناه.

Invoke-Command -ComputerName WEBSRV1 -ScriptBlock { param($foo) & $foo } -ArgumentList $InstallerFilePath

في هذه الحالة ، “يتم ربط” العناصر في مجموعة ArgumentList بالمعلمات المحددة بالترتيب. أسماء المعلمات لا تهم ؛ الأمر المهم هو الترتيب. سيأخذ Invoke-Command العنصر الأول في مجموعة ArgumentList ، ويبحث عن المعلمة الأولى ويربط تلك القيم ، ويفعل الشيء نفسه للثاني ، والثالث وهكذا.

استخدام البناء $Using

$using البنية هي وسيلة شائعة أخرى لتمرير المتغيرات المحلية إلى remote scriptblock. تتيح لك هذه البنية إعادة استخدام المتغيرات المحلية الحالية ببساطة عن طريق وضع اسم المتغير مع تسمية $using:. ليس هناك حاجة للقلق بشأن مجموعة $args أو إضافة كتلة معلمة.

Invoke-Command -ComputerName WEBSRV1 -ScriptBlock { & $using:InstallerFilePath }

في PowerShell، تعتبر بنية $using أبسط بكثير ولكن إذا دخلت في تعلم Pester، سترى أن ArgumentList ستكون صديقك.

Invoke-Command و New-PSSession

في الواقع، يتعلق هذا المنشور فقط بـ Invoke-Command ولكن لتوضيح فائدته، يجب أن نلمس بإيجاز أمر New-PSSession أيضًا. تذكر في وقت سابق أنني ذكرت أن Invoke-Command يمكن أن يستخدم أوامر “ad-hoc” أو استخدام جلسات موجودة.

طوال هذا المنشور، لم نقم سوى بتشغيل أوامر “ad-hoc” على أجهزة الكمبيوتر البعيدة. لقد قمنا بإطلاق جلسة جديدة، تشغيل الكود، وتفكيكها. هذا يعمل بشكل جيد لحالات الاستخدام الفردية ولكن ليس كثيرًا عند قيامك بأداء عشرات الأوامر على نفس الكمبيوتر. في هذه الحالة، من الأفضل إعادة استخدام PSSession موجودة بالفعل عن طريق إنشاء واحدة مسبقًا باستخدام New-PSSession.

قبل تشغيل أي أوامر، ستحتاج أولاً إلى إنشاء جلسة PSSession باستخدام New-PSSession. يمكننا القيام بذلك ببساطة عن طريق تشغيل $session = New-PSSession -ComputerName WEBSRV1. يتم إنشاء جلسة عن بُعد على الخادم بالإضافة إلى مرجع لتلك الجلسة على جهازي المحلي. في هذه المرحلة، يمكنني استبدال مراجع ComputerName بـ Session وتوجيه Session إلى المتغير المحفوظ $session الخاص بي.

Invoke-Command -Session $session -ScriptBlock { & $using:InstallerFilePath }

عند التشغيل، ستلاحظ أن الأداء أسرع لأن الجلسة قد تم إنشاؤها بالفعل. عند الانتهاء، من المهم إزالة الجلسة المفتوحة باستخدام Remove-PSSession.

الملخص

أمر Invoke-Command في PowerShell هو أحد أوامر PowerShell الأكثر شيوعًا وقوة. إنه الأمر الذي أستخدمه شخصيًا بشكل أكبر تقريبًا من بين جميع الأوامر. سهولة الاستخدام والقدرة على تشغيل أي كود على أجهزة الكمبيوتر عن بُعد قوية للغاية وهو أمر أوصي بتعلمه بدقة!

Source:
https://adamtheautomator.com/invoke-command/