在服务器管理工作中,频繁通过图形化远程桌面进行重复性操作效率低下。本文将介绍如何利用 Python 实现 Windows 服务器的自动化远程控制,涵盖主流的 WinRM 与 SSH 两种协议,并提供可直接复用的工具类代码,显著提升运维效率。
方案一:使用 WinRM 与 pywinrm 进行控制
Windows 远程管理(WinRM)是 Windows 系统内置的、基于 WS-Management 协议的远程管理功能,可以理解为为 PowerShell 开放的标准化远程入口。通过 Python 的 pywinrm 库可以方便地调用它。
1. 在目标 Windows 服务器上启用 WinRM
在需要被管理的 Windows 服务器上,以管理员身份打开 PowerShell,执行以下命令:
# 启用 PS 远程管理
Enable-PSRemoting -Force
# 快速配置 WinRM(默认使用 HTTP 5985 端口)
winrm quickconfig
# (可选)如果防火墙未自动放行,可手动添加规则
# netsh advfirewall firewall add rule name="WinRM 5985" dir=in action=allow protocol=TCP localport=5985
对于生产环境,建议配置 HTTPS(5986端口)并使用证书以提升安全性,初期测试可先从 HTTP 开始。
2. 在 Python 端连接并执行命令
首先安装必要的库:
pip install pywinrm
基础连接与命令执行示例:
import winrm
session = winrm.Protocol(
endpoint='http://192.168.1.100:5985/wsman',
transport='ntlm',
username=r'DOMAIN\admin_user',
password='Str0ng_Passw0rd!',
)
shell_id = session.open_shell()
command_id = session.run_command(shell_id, 'ipconfig', ['/all'])
stdout, stderr, rc = session.get_command_output(shell_id, command_id)
print('退出码:', rc)
print('标准输出:\n', stdout.decode('gbk', errors='ignore'))
print('错误输出:\n', stderr.decode('gbk', errors='ignore'))
session.close_shell(shell_id)
关键点说明:
endpoint: 目标服务器的地址与端口。
- 若使用域账户,用户名格式为
DOMAIN\username。
- Windows 命令行输出通常为 GBK 编码,需正确解码以避免乱码。
为了便于复用,可以将连接与操作封装成一个工具类。与处理任何远程服务一样,需要注意连接超时、并发限制等生产环境配置,避免脚本行为不可控。
import winrm
from typing import List, Tuple, Optional
class WindowsRemote:
def __init__(
self,
host: str,
username: str,
password: str,
port: int = 5985,
use_https: bool = False,
domain: Optional[str] = None,
):
self.host = host
self.port = port
self.use_https = use_https
if domain and '\\' not in username:
username = rf"{domain}\\{username}"
scheme = 'https' if use_https else 'http'
endpoint = f'{scheme}://{host}:{port}/wsman'
self._protocol = winrm.Protocol(
endpoint=endpoint,
transport='ntlm',
username=username,
password=password,
server_cert_validation='ignore' if use_https else 'ignore',
)
self._shell_id = self._protocol.open_shell()
def run_cmd(self, command: str, args: Optional[List[str]] = None) -> Tuple[int, str, str]:
args = args or []
cmd_id = self._protocol.run_command(self._shell_id, command, args)
stdout, stderr, rc = self._protocol.get_command_output(self._shell_id, cmd_id)
# 处理编码问题
out = stdout.decode('gbk', errors='ignore') or stdout.decode('utf-8', errors='ignore')
err = stderr.decode('gbk', errors='ignore') or stderr.decode('utf-8', errors='ignore')
return rc, out, err
def run_ps(self, script: str) -> Tuple[int, str, str]:
cmd_id = self._protocol.run_command(self._shell_id, 'powershell', ['-NoProfile', '-NonInteractive', '-'])
self._protocol.send_input(self._shell_id, cmd_id, script.encode('utf-8'))
stdout, stderr, rc = self._protocol.get_command_output(self._shell_id, cmd_id)
out = stdout.decode('utf-8', errors='ignore')
err = stderr.decode('utf-8', errors='ignore')
return rc, out, err
def close(self):
if self._shell_id:
self._protocol.close_shell(self._shell_id)
self._shell_id = None
def __del__(self):
try:
self.close()
except Exception:
pass
使用封装类进行操作的示例:
if __name__ == '__main__':
client = WindowsRemote(
host='192.168.1.100',
username='admin',
password='YourPassword123',
domain='MYDOMAIN', # 若无域环境,此项传 None
)
# 执行CMD命令
rc, out, err = client.run_cmd('hostname')
print('hostname =>', out.strip())
# 执行PowerShell脚本
ps_script = r"""
Get-Service | Where-Object {$_.Status -eq 'Running'} | Select-Object -First 5 | Format-Table -AutoSize
"""
rc, out, err = client.run_ps(ps_script)
print(out)
client.close()
3. 实现文件上传与下载
pywinrm 本身不提供直接的文件传输接口,但可以通过 PowerShell 将文件内容进行 Base64 编码/解码来实现。
上传文件到远程服务器:
import base64
from pathlib import Path
def upload_file(client: WindowsRemote, local_path: str, remote_path: str):
content = Path(local_path).read_bytes()
b64 = base64.b64encode(content).decode('ascii')
ps = f"""
$bytes = [System.Convert]::FromBase64String("{b64}");
[System.IO.File]::WriteAllBytes("{remote_path}", $bytes);
"""
rc, out, err = client.run_ps(ps)
if rc != 0:
raise RuntimeError(f'上传失败: {err}')
从远程服务器下载文件:
def download_file(client: WindowsRemote, remote_path: str, local_path: str):
ps = f"""
$bytes = [System.IO.File]::ReadAllBytes("{remote_path}");
$b64 = [System.Convert]::ToBase64String($bytes);
Write-Output $b64;
"""
rc, out, err = client.run_ps(ps)
if rc != 0:
raise RuntimeError(f'下载失败: {err}')
data = base64.b64decode(out.strip())
Path(local_path).write_bytes(data)
此方案的优点是仅依赖 PowerShell 通道,无需额外开启 SMB 等文件共享服务,更为安全。
方案二:使用 SSH 与 Paramiko
如果目标 Windows 服务器已安装 OpenSSH Server(Windows 10 1809 及 Windows Server 2019 后已内置),则可以使用更为熟悉的 SSH 协议进行管理,其生态工具更为成熟。
1. 在 Windows 上启用 OpenSSH Server
在目标服务器的 PowerShell(管理员)中运行:
# 安装 OpenSSH 服务器组件(若未安装)
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
# 启动服务并设置开机自启
Start-Service sshd
Set-Service -Name sshd -StartupType 'Automatic'
2. 使用 Python Paramiko 库连接
安装 Paramiko 库:
pip install paramiko
连接并执行命令的示例:
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(
hostname='192.168.1.100',
port=22,
username='Administrator',
password='YourPassword123',
)
stdin, stdout, stderr = ssh.exec_command('whoami')
print(stdout.read().decode())
# 使用 SFTP 传输文件
sftp = ssh.open_sftp()
sftp.put('local.txt', r'C:\temp\remote.txt')
sftp.close()
ssh.close()
SSH 方案的优势在于协议通用,工具链丰富,适合需要统一管理异构(Linux/Windows)服务器环境的情况。
方案三:使用 WMI 进行查询与控制
对于需要深度查询系统状态(如进程、服务、硬件信息)的场景,Windows Management Instrumentation (WMI) 提供了非常本地化的接口。
安装依赖库:
pip install wmi pywin32
使用 WMI 查询进程与控制服务的示例:
import wmi
conn = wmi.WMI(
computer="192.168.1.100",
user=r"MYDOMAIN\admin",
password="YourPassword123"
)
# 查询前5个进程
for process in conn.Win32_Process()[:5]:
print(process.ProcessId, process.Name)
# 查询并操作服务(例如:Print Spooler)
for svc in conn.Win32_Service(Name="Spooler"):
print('Print Spooler 状态:', svc.State)
# 停止服务
result, = svc.StopService()
print('停止操作结果:', result)
WMI 功能强大,但 API 较为复杂,更适合用于特定的信息查询与监控场景。
安全实践建议
在自动化脚本中处理远程凭证,务必遵循安全最佳实践:
- 避免硬编码凭证:切勿将密码明文写在代码中。应使用环境变量或加密的配置文件来管理。
import os
PASSWORD = os.getenv('WIN_ADMIN_PASSWORD')
- 遵循最小权限原则:为自动化任务创建专用账户,并授予其完成作业所需的最小权限,避免直接使用域管理员账户。
- 避免日志泄露敏感信息:确保脚本日志不会输出完整的连接字符串、密码等敏感内容。
- 实施连接管理:为脚本配置合理的超时、重试机制,并控制并发连接数,防止脚本异常对服务器造成冲击。
实战整合:构建自动化部署脚本
将上述组件整合,可以构建用于生产环境的自动化运维脚本,例如实现 Windows 服务器的应用部署流程:
- 资产配置:使用
servers.yaml 等配置文件管理服务器列表及其角色。
- 操作封装:利用上述
WindowsRemote 类封装所有远程命令执行与文件传输操作。
- 流程编排:将“停止服务”、“上传文件”、“清理缓存”、“启动服务”、“健康检查”等步骤编写为独立的Python函数,再组合成完整的部署或维护流程。
通过这种自动化方式,原本繁琐的手动远程操作可被转化为一行命令的执行,极大提升了运维工作的可靠性与效率。