找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1072

积分

0

好友

153

主题
发表于 前天 23:54 | 查看: 5| 回复: 0

在服务器管理工作中,频繁通过图形化远程桌面进行重复性操作效率低下。本文将介绍如何利用 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 较为复杂,更适合用于特定的信息查询与监控场景。

安全实践建议

在自动化脚本中处理远程凭证,务必遵循安全最佳实践:

  1. 避免硬编码凭证:切勿将密码明文写在代码中。应使用环境变量或加密的配置文件来管理。
    import os
    PASSWORD = os.getenv('WIN_ADMIN_PASSWORD')
  2. 遵循最小权限原则:为自动化任务创建专用账户,并授予其完成作业所需的最小权限,避免直接使用域管理员账户。
  3. 避免日志泄露敏感信息:确保脚本日志不会输出完整的连接字符串、密码等敏感内容。
  4. 实施连接管理:为脚本配置合理的超时、重试机制,并控制并发连接数,防止脚本异常对服务器造成冲击。

实战整合:构建自动化部署脚本

将上述组件整合,可以构建用于生产环境的自动化运维脚本,例如实现 Windows 服务器的应用部署流程:

  1. 资产配置:使用 servers.yaml 等配置文件管理服务器列表及其角色。
  2. 操作封装:利用上述 WindowsRemote 类封装所有远程命令执行与文件传输操作。
  3. 流程编排:将“停止服务”、“上传文件”、“清理缓存”、“启动服务”、“健康检查”等步骤编写为独立的Python函数,再组合成完整的部署或维护流程。

通过这种自动化方式,原本繁琐的手动远程操作可被转化为一行命令的执行,极大提升了运维工作的可靠性与效率。




上一篇:逻辑回归面试攻略:核心概念与高频考点精讲
下一篇:Python应用容器化部署实战:基于Flask从Dockerfile编写到镜像优化
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-17 17:36 , Processed in 0.120438 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表