Sign in to follow this  
Followers 0
daishi5

Reboot a group of remote machines on a schedule

12 posts in this topic

I am an administrator with several hundred machines that we want to start rebooting on a weekly basis. I have created a reboot script that works well on the local machine, but I forgot that I cannot schedule services with group policy. Now, I am looking at a way to remotely reboot computers based on their location in the AD, and that has a dialogue box to let the user abort the reboot.

I have a local script, I could run a script that runs through AD, copies the script down to all the machines and then uses psexect to start the autoit script. I am not sure if that would be the best way, are there some best practices for something like this, am I overthinking this, forgetting something important, or just going down the wrong road entirely?

Share this post


Link to post
Share on other sites



He did sorta explicitly mention psexec as one of his options in his post...

One way or the other, if you're going to give your end-users the option to opt-out of having their computer reboot, I'm assuming you're going to have to have a local copy of your script on each computer. Why not:

1. Put the script on each computer, then add it to your "Run" registry key so it starts when Windows does.

2. Put a config file (an INI file [see IniRead) in a central location that all of the computers can read from to get their instructions on whether they should prompt for a reboot that week, and when they should do it.

You could also use this as a centralized means of distributing updates to said script if you needed to change anything (as long as you worked that updating mechanism in before you pushed out the initial copy). You could also setup auditing configs, so you could have your script output the results of a reboot prompt (did the user accept it, or deny it). You could also have each computer report its uptime once a day or every few days to make sure that you don't have any that are up for longer than you'd like.

Share this post


Link to post
Share on other sites

Maybe it's me being a noob, but couldn't you just script the shutdown command to run from a server and reboot all the remote PCs?

shutdown -r -m \\<pcname> -t 30

So you could script this to run from the server and loop through for all the PCs you need restarted.

Share this post


Link to post
Share on other sites

One of many ways ;-) Copy from help file and at one line :)

$file = FileOpen("pclist.txt", 0)

If $file = -1 Then
    MsgBox(0, "Error", "Unable to open file.")
    Exit
EndIf

While 1
    $line = FileReadLine($file)
    If @error = -1 Then ExitLoop
    Run (@ComSpec & " /c " & "shutdown.exe -r -m \\" & $line & " -t 120 -f", @SystemDir, @SW_HIDE)
Wend

FileClose($file)

Share this post


Link to post
Share on other sites

Welcome to the AutoIt forum.

Since you said:

> ... let the user abort the reboot

I'll assume that you are planning on automatically rebooting instead of automatically reminding a human to reboot the computer. It is probably too late to convince you not to automatically reboot computers... If you are convinced that your users will have nothing critical running and an automated reboot will do more good than harm - then go for it. The safest method would be to prompt the user to reboot with a stay on top message that re-centers itself (and/or restores itself from being minimized) as often as you wish to annoy the user... but if the user is gone for 4 days, then computer does not reboot automatically without human intervention.

> I am an administrator with several hundred machines ...

> ... that we want to start rebooting on a weekly basis.

> ...

> ... remotely reboot computers based on their location in the AD

Is there really a need to reboot based on AD location? And is it really that critical that these computers reboot that often? The reason that I ask is - your answers determine your solution. If the goal is to simply reboot at a set interval and the world will not come to an end if the computer does not reboot as desired - then:

Push your script out once

Run it once

Upon first run, it checks and (if need be) adds itself to the "Run" reg key

It monitors up time and prompts the user to reboot.

If the user happens to reboot for another reason - that will of course reset the up time. If the user reboots enough, they never see your prompt to reboot. If they do not, they will get reminded by your script/prompt.

Here are some drawbacks to a stand alone local script

[one that does not rely on any outside info]

[does not care about AD location]

[is not pushed out for each launch]

[does not report home or make a log]

[... just "up time" > x = please reboot request]

1) Users might disable it.

2) A power failure could mean that all of the computers want to reboot at very close to the same time one week later. [Randomize the actual reboot time by +/- one day.]

3) You don't get feed back... but you might have other things to do than care about the reboots.

If it is critical that these reboots take place (I cannot image why it would be) then you need some system like what exodius mentioned. I would go with having the local script log stuff locally - but monitor/control the size of the log. Then have a script on one or more servers read/process the local logs one at a time.

Hope this helps.


[size="1"][font="Arial"].[u].[/u][/font][/size]

Share this post


Link to post
Share on other sites

Welcome to the AutoIt forum.

Since you said:

> ... let the user abort the reboot

I'll assume that you are planning on automatically rebooting instead of automatically reminding a human to reboot the computer. It is probably too late to convince you not to automatically reboot computers... If you are convinced that your users will have nothing critical running and an automated reboot will do more good than harm - then go for it. The safest method would be to prompt the user to reboot with a stay on top message that re-centers itself (and/or restores itself from being minimized) as often as you wish to annoy the user... but if the user is gone for 4 days, then computer does not reboot automatically without human intervention.

> I am an administrator with several hundred machines ...

> ... that we want to start rebooting on a weekly basis.

> ...

> ... remotely reboot computers based on their location in the AD

Is there really a need to reboot based on AD location? And is it really that critical that these computers reboot that often? The reason that I ask is - your answers determine your solution. If the goal is to simply reboot at a set interval and the world will not come to an end if the computer does not reboot as desired - then:

Push your script out once

Run it once

Upon first run, it checks and (if need be) adds itself to the "Run" reg key

It monitors up time and prompts the user to reboot.

If the user happens to reboot for another reason - that will of course reset the up time. If the user reboots enough, they never see your prompt to reboot. If they do not, they will get reminded by your script/prompt.

Here are some drawbacks to a stand alone local script

[one that does not rely on any outside info]

[does not care about AD location]

[is not pushed out for each launch]

[does not report home or make a log]

[... just "up time" > x = please reboot request]

1) Users might disable it.

2) A power failure could mean that all of the computers want to reboot at very close to the same time one week later. [Randomize the actual reboot time by +/- one day.]

3) You don't get feed back... but you might have other things to do than care about the reboots.

If it is critical that these reboots take place (I cannot image why it would be) then you need some system like what exodius mentioned. I would go with having the local script log stuff locally - but monitor/control the size of the log. Then have a script on one or more servers read/process the local logs one at a time.

Hope this helps.

Those are some good points, I just got this project handed to me because one of the other admins was not able to get what he wanted with VB script. I went over the meeting minutes, and it looks like we want to reboot the computers because of performance problems, a lot of computers throughout the hospital are not being rebooted until the users have problems.

Making the reboot happen only if the computer has not been rebooted for a while would be good. I wanted to use the AD info because it would prevent me from needing to maintain a seperate list of computers, which I think is very important because lists like that always fail to get maintained. Also, our AD structure is already setup according to the types of computers, so things that should never go down are already in special OUs where I could make sure the script ignores them.

I also just had a thought going over what I have read here, could I make a local script, run it as a service, and if the system has been up more than a week, reboot the computer if no one has been logged on for 5 minutes? The only thing that seems hard about that is determining if no one has been logged on for 5 minutes, but I don't want the computer to reboot if one user logs off and another one wants to log on.

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

I can give you an Example. You can play with the example and change this for your requirement.

#NoTrayIcon
#include <Array.au3>
#Include <date.au3>
Opt("MustDeclareVars", 1)

Dim $list[1]

_getADComputer()
_checkOnline($list)

Func _getADComputer()
    Local $objCommand = ObjCreate("ADODB.Command")
    Local $objConnection = ObjCreate("ADODB.Connection")
    
    $objConnection.Provider = "ADsDSOObject"
    $objConnection.Open("Active Directory Provider")
    $objCommand.ActiveConnection = $objConnection

    Local $strBase = "<LDAP://dc=DOMAIN,dc=LOCAL>"
    Local $strFilter = "(&(&(objectCategory=computer)(|(cn=NB*)(cn=PC*))))"
    Local $strAttributes = "cn"
    Local $strQuery = $strBase & ";" & $strFilter & ";" & $strAttributes & ";subtree"


    $objCommand.CommandText = $strQuery
    $objCommand.Properties("Page Size") = 100
    $objCommand.Properties("Timeout") = 30
    $objCommand.Properties("Cache Results") = False
    Local $ADS_SCOPE_SUBTREE = 2
    $objCommand.Properties("searchscope") = $ADS_SCOPE_SUBTREE

    Local $objRecordSet = $objCommand.Execute

    While Not $objRecordSet.EOF
        If $list[UBound($list) - 1] <> '' Then
            ReDim $list[UBound($list) + 1]
        EndIf

        $list[UBound($list) - 1] = $objRecordSet.Fields("cn" ).Value
        $objRecordSet.MoveNext
    WEnd
    
    $objConnection.Close
    $objConnection = ""
    $objCommand = ""
    $objRecordSet = ""
EndFunc


Func _checkOnline($list)
    Local $var, $i
    For $i = 0 To UBound($list) - 1
        If Ping($list[$i], 20) Then
            ConsoleWrite($list[$i] & " - Uptime:" & _RetrieveUptime($list[$i]) & " User:" & _RetrieveUser($list[$i]) & @CRLF)
        EndIf
    Next
EndFunc


Func _RetrieveUptime($s_Machine)
    Local $LastBootUp, $LocalDateTime, $sec, $uptime, $wbemFlagReturnImmediately = 0x10, $wbemFlagForwardOnly = 0x20
    Dim $objWMIService, $colItems

    $objWMIService = ObjGet("winmgmts:{impersonationLevel=impersonate}!\\" & $s_Machine & "\root\cimv2")
    If IsObj($objWMIService) Then
        $colItems = $objWMIService.ExecQuery("SELECT * FROM Win32_OperatingSystem", "WQL", $wbemFlagReturnImmediately + $wbemFlagForwardOnly)
        For $objItem In $colItems
            $LastBootUp = WMIDateStringToDate($objItem.LastBootUpTime)
            $LocalDateTime = WMIDateStringToDate($objItem.LocalDateTime)
        Next
        $sec = _DateDiff("s", $LastBootUp, $LocalDateTime)
        $uptime = StringFormat("%.02d" & " " & "%.02d" & ":" & "%.02d", Mod($sec / 86400, 3600), Mod($sec / 3600, 24), Mod(($sec / 60), 60))
        $colItems = ""
        $objWMIService = ""
        Return $uptime
    EndIf
EndFunc


Func _RetrieveUser($s_Machine)
    Local $strUser, $aUser
    Dim $objWMIService, $objClass
    $objWMIService = ObjGet("winmgmts:{impersonationLevel=impersonate}!\\" & $s_Machine & "\root\cimv2")
    If IsObj($objWMIService) Then
        $objClass = $objWMIService.ExecQuery("Select * from Win32_ComputerSystem")
        For $obj In $objClass
            $strUser = $obj.UserName
            If $strUser Then
                $aUser = StringSplit($strUser, '\')
                $strUser = $aUser[2]
            EndIf
        Next
        $objClass = ""
        $objWMIService = ""
        Return $strUser
    EndIf
EndFunc


Func WMIDateStringToDate($dtmDate)  
    Return (StringMid($dtmDate, 1, 4) & "/" & StringMid($dtmDate, 5, 2) & "/" & StringMid($dtmDate, 7, 2) & " " & StringMid($dtmDate, 9, 2) & ":" & StringMid($dtmDate, 11, 2) & ":" & StringMid($dtmDate, 13, 2))
EndFunc

_getADComputer() Query the AD

You need to modify $strBase with your domain and strFilter.

With $strFilter I query the Category Computer where the name begins with NB or PC at this Example.

Local $strBase = "<LDAP://dc=DOMAIN,dc=LOCAL>"

Local $strFilter = "(&(&(objectCategory=computer)(|(cn=NB*)(cn=PC*))))"

If $uptime > 5 Days and $strUser = "" then 
    Run (@ComSpec & " /c " & "shutdown.exe -r -m \\" & $line & " -t 120 -f", @SystemDir, @SW_HIDE)
endif

I use some of the function to Query my domain. Get User, Uptime .... If a User call me for help i can see the Computer and can start remote administration.

Edited by Tec

Share this post


Link to post
Share on other sites

Yep, a lot of that goes over my head, I am gonna dig through those examples to make sure I know what it is doing, and I am sure I will have some questions for you in a bit. Thank you for the help.

Share this post


Link to post
Share on other sites

Start with _getADComputer. Query AD and show resulte.

#NoTrayIcon
#include <Array.au3>

Opt("MustDeclareVars", 1)

Dim $list[1][4]

_getADComputer()
_ArrayDisplay($list)

Func _getADComputer()
    Local $objCommand = ObjCreate("ADODB.Command")
    Local $objConnection = ObjCreate("ADODB.Connection")
    
    $objConnection.Provider = "ADsDSOObject"
    $objConnection.Open("Active Directory Provider")
    $objCommand.ActiveConnection = $objConnection

    Local $strBase = "<LDAP://dc=Domain,dc=local>"                 ; Example Query Domain.local 
    ;Local $strBase = "<LDAP://ou=Clients,dc=Domain,dc=local>"     ; Example Query only OU Clients in Domain.local 
    ;Local $strBase = "<LDAP://cn=Computer,dc=Domain,dc=local>"    ; Example Query only CN Computer in Domain.local 
    Local $strFilter = "(objectCategory=computer)"                 ;"(&(&(objectCategory=computer)(|(cn=NB*)(cn=PC*))))"
    Local $strAttributes = "cn,operatingSystem,operatingSystemVersion,operatingSystemServicePack"
    Local $strQuery = $strBase & ";" & $strFilter & ";" & $strAttributes & ";subtree"


    $objCommand.CommandText = $strQuery
    $objCommand.Properties("Page Size") = 100
    $objCommand.Properties("Timeout") = 30
    $objCommand.Properties("Cache Results") = False
    Local $ADS_SCOPE_SUBTREE = 2
    $objCommand.Properties("searchscope") = $ADS_SCOPE_SUBTREE

    Local $objRecordSet = $objCommand.Execute

    While Not $objRecordSet.EOF
        If $list[UBound($list) - 1][0] <> '' Then
            ReDim $list[UBound($list) + 1][4]
        EndIf

        $list[UBound($list) - 1][0] = $objRecordSet.Fields("cn" ).Value
        $list[UBound($list) - 1][1] = $objRecordSet.Fields("operatingSystem" ).Value
        $list[UBound($list) - 1][2] = $objRecordSet.Fields("operatingSystemVersion" ).Value
        $list[UBound($list) - 1][3] = $objRecordSet.Fields("operatingSystemServicePack" ).Value
        $objRecordSet.MoveNext
    WEnd
    
    $objConnection.Close
    $objConnection = ""
    $objCommand = ""
    $objRecordSet = ""
EndFunc

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

Start with _getADComputer. Query AD and show resulte.

#NoTrayIcon
#include <Array.au3>

Opt("MustDeclareVars", 1)

Dim $list[1][4]

_getADComputer()
_ArrayDisplay($list)

Func _getADComputer()
    Local $objCommand = ObjCreate("ADODB.Command")
    Local $objConnection = ObjCreate("ADODB.Connection")
    
    $objConnection.Provider = "ADsDSOObject"
    $objConnection.Open("Active Directory Provider")
    $objCommand.ActiveConnection = $objConnection

    Local $strBase = "<LDAP://dc=Domain,dc=local>"                 ; Example Query Domain.local 
    ;Local $strBase = "<LDAP://ou=Clients,dc=Domain,dc=local>"     ; Example Query only OU Clients in Domain.local 
    ;Local $strBase = "<LDAP://cn=Computer,dc=Domain,dc=local>"    ; Example Query only CN Computer in Domain.local 
    Local $strFilter = "(objectCategory=computer)"                 ;"(&(&(objectCategory=computer)(|(cn=NB*)(cn=PC*))))"
    Local $strAttributes = "cn,operatingSystem,operatingSystemVersion,operatingSystemServicePack"
    Local $strQuery = $strBase & ";" & $strFilter & ";" & $strAttributes & ";subtree"


    $objCommand.CommandText = $strQuery
    $objCommand.Properties("Page Size") = 100
    $objCommand.Properties("Timeout") = 30
    $objCommand.Properties("Cache Results") = False
    Local $ADS_SCOPE_SUBTREE = 2
    $objCommand.Properties("searchscope") = $ADS_SCOPE_SUBTREE

    Local $objRecordSet = $objCommand.Execute

    While Not $objRecordSet.EOF
        If $list[UBound($list) - 1][0] <> '' Then
            ReDim $list[UBound($list) + 1][4]
        EndIf

        $list[UBound($list) - 1][0] = $objRecordSet.Fields("cn" ).Value
        $list[UBound($list) - 1][1] = $objRecordSet.Fields("operatingSystem" ).Value
        $list[UBound($list) - 1][2] = $objRecordSet.Fields("operatingSystemVersion" ).Value
        $list[UBound($list) - 1][3] = $objRecordSet.Fields("operatingSystemServicePack" ).Value
        $objRecordSet.MoveNext
    WEnd
    
    $objConnection.Close
    $objConnection = ""
    $objCommand = ""
    $objRecordSet = ""
EndFunc
Ok, I am pretty sure I get what everything is doing, but where do I get _ArrayDisplay($list) from? I hope this isn't a stupid question that I overlooked the obvious on. Edit: nevermind, my copy paste skills failed me, I see that I did not get the include in there. Edited by daishi5

Share this post


Link to post
Share on other sites

... The only thing that seems hard about that is determining if no one has been logged on for 5 minutes, but I don't want the computer to reboot if one user logs off and another one wants to log on.

Maybe the script in this post will work for you:

http://www.autoitscript.com/forum/index.ph...showtopic=79148


[size="1"][font="Arial"].[u].[/u][/font][/size]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0