rasdial 显示已连接。网络究竟何时才真正就绪?
退出码 0 只说明拨号完成,并不说明文件共享已经应答。先抓取本机返回值,再探测作业所依赖的资源。
打开 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" }
把退出码和非机密的错误文本一起记入日志,因为条目缺失、凭据被拒和超时需要不同的应对。下表把常见结果对应到下一步动作。
| Result | What it indicates | Next step |
|---|---|---|
| 0 | The dial completed | Probe the resource, do not start writing yet |
| 691 | The credential or authentication method was rejected | Fix the account or method, do not retry in a loop |
| 623 | The named entry was not found | Fix the profile name or its scope |
| Other non-zero | Transport, timeout or adapter failure | Read the message, then check the path |
就绪的是资源,而不是隧道
拨号成功后,要测试作业真正需要的东西。共享目录、API 端点或数据库端口,比公共网站更适合作为就绪探测:公共站点经由任何可用路径都能应答,对私有路径毫无证明力。选择应用本身所依赖的那项探测。TCP 连接成功说明路由和过滤是通的,而第一次写入所需的大部分条件正是这个:
Test-NetConnection -ComputerName files.contoso.example -Port 445
限制尝试次数,并记录是哪一阶段失败。凭据被拒应当让脚本停下,而不是喂给重试循环;尚未出现的 DNS 名称值得几次短暂重试;始终打不开的端口,是客户端之上的路由或策略问题。
如果连接在作业开始前就已存在,就不要动它。断开一个并非由你建立的会话会打断用户的其他工作,所以要记录会话归谁、以及怎样收尾才合适。
把配置文件限定在运行作业的账户下
计划任务以自己的身份和会话运行,在你桌面上可用的配置文件在那里可能根本不可见。先以任务账户重新执行可见性检查和拨号,再信任这个计划。密码也不要放在命令行上:参数对其它进程可见,而且常被写进日志。
如果部署需要浏览器提示、智能卡触碰或 MFA 批准,那段交互就是设计的一部分,无法靠模拟按键绕开。遇到这种情况请使用受支持的客户端,让需要人做的那一步真的发生。
最终脚本的形态仍然很短:检查配置文件、拨号、读退出码、探测资源,然后才开始干活。每一步都回答了上一步无法回答的问题,而其中没有一步依赖 sleep。