前言

在管理添加了域控的计算机时,经常会遇到由于域的权限设置导致普通用户的电脑无法安装应用程序,切换到管理员账号下安装又比较繁琐,非系统管理员或者又计算机维护基础的人员基本无法操作。我在实际的运维过程中遇到了大量的因为应用程序损坏需要重装的报障,这些报障大大提高了我的工作量,于是我考虑使用自动化的方法,将使用域管理员账户启动CMD并通过CMD从局域网服务器获取程序安装包,然后静默安装所需要的应用程序,整个过程自动执行,无需任何点击和输入。当计算机的使用者发现有某个应用程序无法使用的时候,仅需要双击运行对应的一键安装程序即可完成重装应用程序的操作。进而减少报障数量及我自己的工作量。

分析

在有域控的电脑上手动安装应用程序的流程如下:

  1. 切换登录用户为管理员账户。
  2. 从局域网服务器上获取对应的安装程序。
  3. 运行安装程序。
  4. 选择安装路径并点击下一步直至安装完成。
  5. 注销管理员账户登录,并交付用户使用。

在手动安装流程中并非所有步骤都可以通过自动化程序实现由于Windos的权限设置在切换用户的界面上无法使用运行自动化程序。所以需要使用其他思路来实现自动化。分析根本诉求是需要在域管理员的权限下执行某个安装程序,于是采用通过域管理员启动CMD,通过CMD打开所需安装程序。使用静默安装参数或者模拟点击的方式使整个安装流程不需人为操作,以达到无人值守安装的目的。

所以在有域控的电脑上自动化无人值守安装的流程应该如下:

  1. 从局域网服务器中获取安装包到指定目录。
  2. 使用域管理员权限打开cmd。
  3. 在CMD中打开所需安装程序。
  4. 当所需安装的程序打开后关闭域管理员权限的CMD。
  5. 自动安装至安装结束。

实现

目录创建及文件下载

  1. 从FTP服务器下载文件之前需要现在C盘中建立一个临时目录,使用os库来操作系统功能。首先检测目录是否存在,如不存在则创建目录。
if os.path.exists(directory):
    print("目录已存在,无需创建!")
else:
    os.makedirs(directory)
  1. 检查需要的文件在本地是否已经存在,如果本地文件存在则关闭FTP链接。
if os.path.exists(directory + filename):
    print("目标文件已存在无需下载!")
    ftp.quit()
  1. 使用ftplib库下载所需的文件,tqdm来显示进度条。TYPE I参数使得FTP以二进制传输。
# 连接到FTP服务器
ftp = ftplib.FTP(FTP_SERVER)
ftp.login(FTP_USERNAME, FTP_PASSWORD)
    # 获取文件大小
    ftp.voidcmd('TYPE I')
    total_size = ftp.size(filename)

    # 下载文件并显示进度条
    with open(directory + filename, 'wb') as f:
        with tqdm(total=total_size, unit='B', unit_scale=True, desc=filename) as pbar:
            ftp.retrbinary('RETR ' + filename, callback)
            # 关闭FTP连接
            ftp.quit()

通过管理员权限运行CMD安装指定程序

通过使用OS库来来启动CMD,并使用runas命令来运行已经保存到本地的安装程序。runas命令可以时非管理员用户以管理员身份运行某个程序。runas命令的用法如下:
RUNAS [ [/noprofile | /profile] [/env] [/savecred | /netonly] ] /user:<UserName> program

为了使安装程序可以静默安装,需要在安装程序的路径后配置静默安装参数,通常来说静默安装的参数为/S。但是并非全部安装程序都可以使用这个安装参数,例如WPS需要先将官网下载的安装程序中的/&TEMP/setup.exe文件提取出来,使用/S -agreelicense参数进行静默安装。

def open_cmd():
    # 定义一个命令字符串,用于运行runas命令
    command = 'runas /user:yshome\\2084 "C://0Aatemp/(your app name) /S" '
    # 打开cmd窗口,并执行命令
    os.system('start cmd /K ' + command)

自动输入域管理员账户密码

在runas命令中,用户密码不能通过参数传入,因此在执行通过指定账户执行某个应用程序时需要用户手动输入对应账户的密码。这将导致无法实现全自动静默安装,所以我通过使用pyautogui库来实现在cmd窗口模拟输入,以此来完成全自动输入密码。但是请注意,这样会使得密码以明文的形式存在,且用户可以通过在自动输入密码时将窗口切换至记事本等方式获取明文的密码。输入密码后通过输入shift键的方式来绕过中文输入法。

def input_password():
    # 等待一秒,确保cmd窗口已经打开
    pyautogui.time.sleep(2)
    # 输入密码
    pyautogui.write('yours password')
    # 按下两次回车键
    pyautogui.press('shift')
    pyautogui.press('enter')

启动对应静默安装进程后关闭CMD窗口

这里通过检测对应安装程序的进程是否存在来实现,如果存在静默安装程序的进程则关闭CMD窗口。

def kill_cmd():
    for proc in psutil.process_iter(['pid', 'name']):
        if proc.info['name'] == 'yours app name':
            os.system('taskkill /f /im cmd.exe')
            break

问题及展望

部分应用程序无法使用/S静默安装的问题

对于这些不能使用静默安装参数的应用程序,这里可以使用pyautogui的模拟点击来自动点击安装和下一步等操作,如pyautogui.click([958, 639])。或者将对应的应用程序解包,重新打包为自解压压缩程序来实现静默安装。