打开 VPN 窗口后睡十秒的脚本只是在猜,而且两个方向都猜错:要么在隧道可用之前就开始复制,要么在工作本可开始时仍在等待。两种结果都不能说明认证是否已经完成。先用命令自身的返回值,再向服务提出一个它确实能回答的问题。这两个步骤的先后顺序就是全部设计。

这条命令究竟报告了什么

对于已安装的命令行工具能够识别的本机 Windows 配置文件,rasdial.exe 会拨叫一个命名条目并返回进程退出码。该退出码描述的是这次拨号尝试:零表示该配置文件的连接已经建立,非零表示它在退出码所指的阶段失败。要把它看作一层的返回值,而不是整个作业的结果。

这个结果完全没有涉及地址分配、私有 DNS、新增路由,也没有涉及能否访问某台特定服务器。这些都发生在拨号成功之后,其中任何一项仍可能出错。正是这段空白,让已连接图标和复制失败经常同时出现。

先抓住返回值,再让别的任何东西运行

先确认配置文件存在于将要运行作业的那个账户下,而不只是你当前测试用的账户:

Get-VpnConnection -Name "Contoso-Office"
Get-VpnConnection -Name "Contoso-Office" -AllUserConnection

然后拨号并立刻读取退出码。PowerShell 只在下一条外部命令运行之前把本机结果保存在 $LASTEXITCODE 中,所以拨号与检查之间一次随手的 ping 就会毁掉你想读的值。先把结果赋给你自己的变量,再决定怎么做。

& rasdial.exe "Contoso-Office"
$dial = $LASTEXITCODE
if ($dial -ne 0) { throw "rasdial failed with $dial" }

把退出码和非机密的错误文本一起记入日志,因为条目缺失、凭据被拒和超时需要不同的应对。下表把常见结果对应到下一步动作。

ResultWhat it indicatesNext step
0The dial completedProbe the resource, do not start writing yet
691The credential or authentication method was rejectedFix the account or method, do not retry in a loop
623The named entry was not foundFix the profile name or its scope
Other non-zeroTransport, timeout or adapter failureRead the message, then check the path

就绪的是资源,而不是隧道

拨号成功后,要测试作业真正需要的东西。共享目录、API 端点或数据库端口,比公共网站更适合作为就绪探测:公共站点经由任何可用路径都能应答,对私有路径毫无证明力。选择应用本身所依赖的那项探测。TCP 连接成功说明路由和过滤是通的,而第一次写入所需的大部分条件正是这个:

Test-NetConnection -ComputerName files.contoso.example -Port 445

限制尝试次数,并记录是哪一阶段失败。凭据被拒应当让脚本停下,而不是喂给重试循环;尚未出现的 DNS 名称值得几次短暂重试;始终打不开的端口,是客户端之上的路由或策略问题。

如果连接在作业开始前就已存在,就不要动它。断开一个并非由你建立的会话会打断用户的其他工作,所以要记录会话归谁、以及怎样收尾才合适。

把配置文件限定在运行作业的账户下

计划任务以自己的身份和会话运行,在你桌面上可用的配置文件在那里可能根本不可见。先以任务账户重新执行可见性检查和拨号,再信任这个计划。密码也不要放在命令行上:参数对其它进程可见,而且常被写进日志。

如果部署需要浏览器提示、智能卡触碰或 MFA 批准,那段交互就是设计的一部分,无法靠模拟按键绕开。遇到这种情况请使用受支持的客户端,让需要人做的那一步真的发生。

最终脚本的形态仍然很短:检查配置文件、拨号、读退出码、探测资源,然后才开始干活。每一步都回答了上一步无法回答的问题,而其中没有一步依赖 sleep。

相关文章