[WSUS] Script Powershell pour configurer un client

Voici un petit script PowerShell que j’ai fait afin de paramétrer automatiquement des clients pour se connecter à un serveur WSUS

    # Create the RegKeys used by the computer to check the Update from your WSUS
    # Author: KueiSaho
    # Version: v1.0 - 21/03/2011

    ##########################################################################################################################################
    #HELP on registry values
##########################################################################################################################################
    #AUOptions			Range = 2|3|4|5
    #2 = Notify before download.
    #3 = Automatically download and notify of installation.
    #4 = Automatic download and scheduled installation. (Only valid if values exist for ScheduledInstallDay and ScheduledInstallTime.)
    #5 = Automatic Updates is required, but end users can configure it.

    #AutoInstallMinorUpdates		Range = 0|1
    #0 = Treat minor updates like other updates.
    #1 = Silently install minor updates.

    #DetectionFrequency		Range=n; where n=time in hours (1-22).
    #Time between detection cycles.

    #DetectionFrequencyEnabled	Range = 0|1
    #1 = Enable DetectionFrequency.
    #0 = Disable custom DetectionFrequency (use default value of 22 hours).

    #NoAutoRebootWithLoggedOnUsers	Range = 0|1;
    #1 = Logged-on user gets to choose whether or not to restart his or her computer.
    #0 = Automatic Updates notifies user that the computer will restart in 5 minutes.

    #NoAutoUpdate			Range = 0|1
    #0 = Enable Automatic Updates.
    #1 = Disable Automatic Updates.

    #RebootRelaunchTimeout		Range=n; where n=time in minutes (1-1440).
    #Time between prompting again for a scheduled restart.

    #RebootRelaunchTimeoutEnabled	Range = 0|1
    #1 = Enable RebootRelaunchTimeout.
    #0 = Disable custom RebootRelaunchTimeout(use default value of 10 minutes).

    #RebootWarningTimeout		Range=n; where n=time in minutes (1-30).
    #Length, in minutes, of the restart warning countdown after installing updates with a deadline or scheduled updates.

    #RebootWarningTimeoutEnabled	Range = 0|1
    #1 = Enable RebootWarningTimeout.
    #0 = Disable custom RebootWarningTimeout (use default value of 5 minutes).

    #RescheduleWaitTime		Range=n; where n=time in minutes (1-60).
    #Time, in minutes, that Automatic Updates should wait at startup before applying updates from a missed scheduled installation time.
    #Note that this policy applies only to scheduled installations, not deadlines. Updates whose deadlines have expired should always be installed as soon as possible.

    #RescheduleWaitTimeEnabled	Range = 0|1
    #1 = Enable RescheduleWaitTime
    #0 = Disable RescheduleWaitTime(attempt the missed installation during the next scheduled installation time).

    #ScheduledInstallDay		Range = 0|1|2|3|4|5|6|7
    #0 = Every day.
    #1 through 7 = The days of the week from Sunday (1) to Saturday (7).
    #(Only valid if AUOptions equals 4.)

    #ScheduledInstallTime		Range = n; where n = the time of day in 24-hour format (0-23).

    #UseWUServer			The WUServer value is not respected unless this key is set.
    ##########################################################################################################################################

    $WSUSServerURL = “http://10.0.1.1:8530”
    $WindowsUpdateKey = test-path "HKLM:\SOFTWARE\Policies\Microsoft\windows\WindowsUpdate"
    $WSUSAUOption = "4"
    $WSUSDetectionFrequency = "16"
    $WSUSDetectionFrequencyEnabled = "1"
    $WSUSNoAutoUpdate = "0"
    $WSUSScheduledInstallDay = "1"
    $WSUSScheduledInstallTime =  "3"
    $WSUSUseWUServer = "1"

    if($WindowsUpdateKey -eq $False)
    {
        Write-Host "Create the new Key WindowsUpdate"
        $WindowsKey = New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\windows\WindowsUpdate"
        $WindowsKeyAU = New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\windows\WindowsUpdate\AU"
    }

    # Regenerate WSUSID
    # Do not use
    # Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\WindowsUpdate\SusClientId" | Remove-ItemProperty -Force -WhatIf
    # C:\WINDOWS\system32\wuauclt /resetauthorization /detectnow

    # Create a key with the value:
    Set-ItemProperty -path "HKLM:\SOFTWARE\Policies\Microsoft\windows\WindowsUpdate" -name "WUServer" -value $WSUSServerURL
    Set-ItemProperty -path "HKLM:\SOFTWARE\Policies\Microsoft\windows\WindowsUpdate" -name "WUStatusServer" -value $WSUSServerURL
    Set-ItemProperty -path "HKLM:\SOFTWARE\Policies\Microsoft\windows\WindowsUpdate\AU" -name "AUOptions" -value $WSUSAUOptions
    Set-ItemProperty -path "HKLM:\SOFTWARE\Policies\Microsoft\windows\WindowsUpdate\AU" -name "DetectionFrequency" -value $WSUSDetectionFrequency
    Set-ItemProperty -path "HKLM:\SOFTWARE\Policies\Microsoft\windows\WindowsUpdate\AU" -name "DetectionFrequencyEnabled" -value $WSUSDetectionFrequencyEnabled
    Set-ItemProperty -path "HKLM:\SOFTWARE\Policies\Microsoft\windows\WindowsUpdate\AU" -name "NoAutoUpdate" -value $WSUSNoAutoUpdate
    Set-ItemProperty -path "HKLM:\SOFTWARE\Policies\Microsoft\windows\WindowsUpdate\AU" -name "ScheduledInstallDay" -value $WSUSScheduledInstallDay
    Set-ItemProperty -path "HKLM:\SOFTWARE\Policies\Microsoft\windows\WindowsUpdate\AU" -name "ScheduledInstallTime" -value $WSUSScheduledInstallTime
    Set-ItemProperty -path "HKLM:\SOFTWARE\Policies\Microsoft\windows\WindowsUpdate\AU" -name "UseWUServer" -value $WSUSUseWUServer

    # Reload the Windows Update CLient configuration
    sc stop wuauserv
    sc start wuauserv
    C:\WINDOWS\system32\wuauclt /detectnow
Max-Hebergs - Hebergeur bas prix et complet!

[Visual Studio 2008] Bug lors d’une installation unattend

Sur ce coup, je hais Microsoft !!! :cry:

Tout d’abord il faut savoir que l’installeur de Visual Studio est buggué dans les version 2005 et 2008. Dans mon cas, je voulais réaliser une installation automatisée avec un fichier de réponse de Visual Studio 2008.

Pour ce faire nous générons le fichier unattend:

d:\VS2008\Setup\setup.exe /createunattend d:\VS2008\Setup\vs2008_deploy.ini

Une interface graphique doit se lancer vous demandant les composants que vous souhaiter installer. Une fois valider, l’installeur génère le fichier de réponse.

A présent nous allons l’utiliser :

d:\VS2008\Setup\setup.exe /unattendfile d:\VS2008\Setup\vs2008_deploy.ini

Théoriquement, l’installation doit se dérouler en arrière plan, le seul moyen de voir ce qu’il se passe est de regarder l’observateur d’évènement et les logs (%tmp%).

C’est là que l’installeur s’arrête avec dans les logs d’erreur ce message :

Setup.exe: GetGlobalCustomProperty – Property: {383F0141-C682-4665-A69B-756E719C968D} – PropertyName: Process Return Code – Value: 1603

Pas très parlant…

Finalement en épluchant bien les logs, j’ai compris que l’installeur essaye d’installer les framework .net alors qu’il est déjà installé et à jour, du coup il quitte avec un code d’erreur. Et effectivement, en regardant le fichier de réponse, on voit bien qu’il demande d’installer les framework .net (3.5 dans mon cas).

Voici donc le workarround que j’ai trouvé :

- dans les sections [PreInstallOrder]/[InstallOrder]/[PostInstallOrder] effacer toute allusion au mot framework
- ensuite dans les section [gfn_mid net framework setup]/[gfn_mid net framework v3.5]/[gfn_mid net framework setup x64] changer l’option InstallActionInteger=1 en InstallActionInteger=5

Si vous relancez l’installation automatisée, elle doit se dérouler sans problème normalement.

[Windows/PowerShell] Création automatique d’un domaine Active Directory

Ça ne doit pas être utile à beaucoup de gens, mais il est possible de créer un domaine Active Directory de façon automatisé.
Tout se passe avec le switch /unattend de la commande dcpromo. On peut faire beaucoup de choses avec (créer un domain enfant, ajouter des DC, etc etc). Pour plus d’informations, vous pouvez voir cette KB Microsoft.

Ici j’ai réalisé un petit script PowerShell qui permet de faciliter la génération d’un fichier de réponse et ensuite la création du domaine.


# Generate response file for Active Directory automated creation
# Author: KueiSaho
# Version: v1.0

Write-Host "This script generate a response file for an automated Active Directory creation and execute the automated process"

$DNSdomainname = Read-Host "Enter DNS Domain name"
$NetBiosdomainname = Read-Host "Enter NetBios Domain name"
$SafePassword = Read-Host "Enter the Safe Mode Password"

# Location of response file
$file = new-item "c:\temp\winnt.inf" –type file -force
Add-Content $file "[DCINSTALL]"
Add-Content $file "InstallDNS=yes"
Add-Content $file "NewDomain=forest"
Add-Content $file "NewDomainDNSName=$DNSdomainname"
Add-Content $file "DomainNetBiosName=$NetBiosdomainname"
Add-Content $file "ReplicaOrNewDomain=domain"
Add-Content $file "RebootOnCompletion=yes"
Add-Content $file "SafeModeAdminPassword=$SafePassword"

Write-Host "`r"
Write-Host "`a"
Write-Host "OK - Response file $file has been created."
Write-Host "`r"

$message = "Now do you want to create $DNSdomainname domain ? /!\ The server will reboot without warning /!\"
$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
"Create $DNSdomainname domain"
$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
"Stop process."
$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
$result = $host.ui.PromptForChoice($title,$message, $options, 1)
switch ($result)
{
0 {Invoke-Expression "dcpromo /unattend:$file"}
1 {"OK - No creation of $DNSdomainname domain, the reponse file location is $file "}
}

[Windows] Envoyer un mail à chaque ouverture de session

Je suis actuellement sur un projet où le compte administrateur du domaine est connu de tous :-/.
Pour des raisons propres au client (jobs importants qui tournent en tant qu’Administrateur du domaine), nous ne pouvons changer encore le mot de passe dans l’immédiat.

Du coup, pour être sur que personne n’utilise ce compte en cachette, et pour ne pas à avoir à checker tous les logs de sécurité, j’ai mis en place un petit script afin qu’à chaque ouverture de session avec le compte administrateur du domaine, un mail soit envoyé.
Pour ce faire, il faut d’abord récupérer l’utilitaire blat.exe

Puis créer un petit cmd contenant :


echo %USERDOMAIN%\%USERNAME% connecté sur %COMPUTERNAME% le %date% %time% depuis %CLIENTNAME% >c:\logon.txt
 \\domaine\RACINEDFS\ADMINS\blat\full\blat.exe c:\logon.txt -t admin@domaine.com -server smtp.domaine.com -f administrateur@domaine.com
exit (0)

Il ne reste plus qu’à mettre en logonscript de l’administrateur du domaine ce petit cmd.

[Bash/DOS/PowerShell] Récupérer le code retour d’une commande

Pour récupérer le code retour d’une commande qui vient d’être lancée :

En BASH/KSH:

echo $?

En DOS :

echo %ERRORLEVEL%

PowerShell :

Write-Host $?

[Windows] Supprimer les fichiers de plus de N jours

forfiles.exe est un outil très utile pour exécuter une ou plusieurs commandes sur un ensemble de fichiers.
Dans notre exemple, nous allons chercher à supprimer les fichiers de LOG de plus de 30 jours :

c:\windows\system32\forfiles.exe -p I:\LOGS -m *.log -d -30 -c "cmd.exe /C del @path\"

Il ne reste plus qu’à le mettre en tâche planifié avec at par exemple pour automatiser le ménage.

[Windows/VBA] Modifier les fichiers et leur modèle/template associé

Toujours dans le cadre de mon histoire de fichiers accédant à des modèles hébergés sur un serveur à arreter, je me suis résolu à faire un script VBA qui modifie les fichiers Word afin de pointer sur le bon template/modèle.

'ModifTemplateV2

Public Function RecursiveDir(colFiles As Collection, _
                             strFolder As String, _
                             strFileSpec As String, _
                             bIncludeSubfolders As Boolean)

Dim strTemp As String
Dim colFolders As New Collection
Dim vFolderName As Variant
Dim lAttr As Long

    'Add files in strFolder matching strFileSpec to colFiles
    strFolder = TrailingSlash(strFolder)
    On Error Resume Next
    strTemp = Dir(strFolder & strFileSpec)
    If Err.Number = 0 Then
        On Error GoTo 0
        Do While strTemp <> vbNullString
            colFiles.Add strFolder & strTemp
            strTemp = Dir
        Loop

        If bIncludeSubfolders Then
            'Fill colFolders with list of subdirectories of strFolder

            strTemp = Dir(strFolder, vbDirectory)
            Do While strTemp <> vbNullString
                If (strTemp <> ".") And (strTemp <> "..") Then
                    On Error Resume Next
                    lAttr = GetAttr(strFolder & strTemp)
                    If Err.Number = 0 Then
                        On Error GoTo 0
                        If (lAttr And vbDirectory) <> 0 Then
                            colFolders.Add strTemp
                        End If
                    Else
                        Write #6, Err.Number & ": " & strFolder & strTemp
                        On Error GoTo 0
                    End If
                End If
                strTemp = Dir
            Loop

            'Call RecursiveDir for each subfolder in colFolders
            For Each vFolderName In colFolders
                Call RecursiveDir(colFiles, strFolder & vFolderName, strFileSpec, True)
            Next vFolderName
        End If
    Else
        Write #6, Err.Number & ": " & strFolder & strFileSpec
        On Error GoTo 0
    End If
End Function

Public Function TrailingSlash(strFolder As String) As String
    If Len(strFolder) > 0 Then
        If Right(strFolder, 1) = "\" Then
            TrailingSlash = strFolder
        Else
            TrailingSlash = strFolder & "\"
        End If
    End If
End Function

Sub ModifTemplate()

    Dim strFilePath As String
    Dim strPath As String
    Dim intCounter As Integer
    Dim strFileName As String
    Dim objDoc As Document
    Dim objTemplate As template
    Dim dlgTemplate As Dialog
    Dim nServer As Integer
    Const logfile As String = "C:\temp\exec\result-modif.log"
    Const errFile As String = "C:\temp\exec\error.log"
    Dim colFiles As New Collection
    Dim AppWord As Word.Application
    OldServer = "\\server\modeles"
    NewServer = "\\oldserver\MODELES"
    Dim iLenStrOldServer As Integer         'Nombre de caractere de la chaine de l'ancien serveur
'On Error GoTo errLbl
'On Error Resume Next
    Const sLogFichierLong As String = "C:\temp\exec\fichierstroplongs.log"

    iLenStrOldServer = Len(OldServer)

    strFilePath = InputBox("Où se trouvent les documents à modifier ?")
    If Dir(logfile) <> "" Then Kill logfile
    Open sLogFichierLong For Output As #6
    RecursiveDir colFiles, strFilePath, "*.doc", True
    Close #6
    Open logfile For Append As #1
    Open errFile For Output As #2

    Write #1, "Debut de la modification des modeles des Documents Word se trouvant dans " & strFilePath
    Print #1, Format(Now, "dd/mm/yyyy hh:mm:ss")

    Set AppWord = New Word.Application
    AppWord.AutomationSecurity = msoAutomationSecurityForceDisable

    For Each vfile In colFiles
        If Not IsFileLocked(vfile) Then
            If Not InStr(vfile, "~") > 0 Then
                Set objDoc = AppWord.Documents.Open(vfile, , False)
                Set objTemplate = objDoc.AttachedTemplate
                If LCase(Left(objTemplate.Path, iLenStrOldServer)) = LCase(OldServer) Then
                    objDoc.AttachedTemplate = NewServer & Mid(objTemplate.FullName, iLenStrOldServer + 1)
                    Write #1, "Modification de " & vfile
                    objDoc.Save
                End If
                Debug.Print vfile
                objDoc.Close
            End If
        Else
            Write #2, vfile
        End If
        DoEvents
    Next vfile

   AppWord.Quit
   Set AppWord = Nothing
    Write #1, "Fin analyse Documents Word"
    Print #1, Format(Now, "dd/mm/yyyy hh:mm:ss")
    Close #1 ' Close log file.
    Close #2 'Close error file

   Set objDoc = Nothing
   Set objTemplate = Nothing
Exit Sub
errLbl:
    MsgBox "L'indexation a planté"
    If Not (AppWord Is Nothing) Then
        AppWord.Quit
        Set AppWord = Nothing
    End If

End Sub

Private Function IsFileLocked(ByVal sFile As String) As Boolean
    On Error Resume Next

     ' \\ Open the file
    Open sFile For Binary Access Read Write Lock Read Write As #3
     ' \\ Close the file
    Close #3

     ' \\ If error occurs the document if open!
    If Err.Number <> 0 Then
         ' \\ msgbox for demonstration purposes
        'MsgBox Err.Description, vbExclamation, "Warning File is opened"

         '\\ Return true and clear error
        IsFileLocked = True
        Err.Clear
    Else
        IsFileLocked = False
    End If
End Function

Désolé si le script est un peu « sale », mais si vous avez des questions, n’hésitez pas ^^

PS : je n’ai pas réussi à gérer l’ouverture de fichier « corrompue », du coup le seul moyen que j’ai trouvé, est de passer en mode debogage et de déplacer le curseur sur Next vfile

[Windows] Fichiers word qui essayent d’accéder à une ressource réseau à l’ouverture

Ça ça a été un problème bien tordu !

Je vous remets dans le contexte.

Nous avons migré des fichiers utilisateurs d’un ancien serveur Windows 2000 vers une VM Windows 2003 (tout ça c’est du détail).

Les utilisateurs ont pu retrouver leurs bébés et travailler normalement. Mais lorsque nous nous sommes décidé à éteindre l’ancien serveur 2000, beaucoup d’utilisateurs ce sont plein de ne pouvoir ouvrir leurs fichiers. Nous avons mis un peu de temps avant de faire le rapprochement. Ce qui est sûr c’est qu’en rallumant le serveur, le fichiers s’ouvraient à nouveau.

La seconde étape a été de déterminer ce que cherchait ces fichiers Word sur ce serveur. Avec un Analyseur réseau, nous avons pu déterminer que ces fichiers Word essayaient d’accéder à un partage hébergeant des Modèles.

Du coup plusieurs solutions se sont présentés à nous :

1/ faire un bête Alias de l’ancien nom du serveur vers le nouveau (bahhhhh)

2/ demander à tous les utilisateurs de modifier leurs fichiers en passant par :

Outils –> Modèles et compléments –> Modèles du document

et corriger avec le nouveau chemin vers les modèles (mais bien sûr, dans 10 ans ce ne sera pas fait…)

3/ passer par un script VBS/VBA qui modifiera tous les fichiers récursivement, pour ce faire, il faut modifier la propriété AttachedTemplate vous pouvez trouver des exemples de scripts ici ou

Cette solution est forcement la meilleure, mais je n’ai pas osé la mettre en place étant donné le nombre de fichiers à modifier et mon inexpérience sur les techno Office (peur de corrompre les fichiers utilisateurs).
Edit : finalement j’ai fais un script, ça me démangeait :-)

4/ Au final nous avons décidé de mettre en place un DFS de consolidation. Ce type de DFS agit un peu comme un Alias mais un niveau d’un partage. C’est plus propre qu’un Alias et plus facile à maintenir.

Je ne détaillerai sa mise en place ici car d’autres sites le font bien mieux.

Donc si un jour vous avez des fichiers Word qui mettent du temps à s’ouvrir, voir ne s’ouvrent pas du tout, pensez aux modèles :mrgreen:

Edit : voici une KB Microsoft qui explique également comment résoudre ou contourner le problème : KB830561

[Windows] Equivalent à « id -a »

Je sais, c’est un peu débile de faire un article pour ça, mais je viens de découvrir l’équivalent à « id -a » (commande Unix/Linux qui permet de savoir de quel(s) groupe(s) est membre un compte ) sous Windows.

Il s’agit de whoami /all

Je trouve même qu’il est trop verbeux, mais c’est bien utile pour déterminer les droits et privilèges d’un utilisateur.

A noter que whoami n’est pas de base dans Windows, il faut installer le Resource Kit.

[Windows/AD] Lister les utilisateurs et leur homedir

Aujourd’hui nous avons réalisé qu’un certain nombre d’utilisateurs avaient une mauvaise homedir dans notre Active Directory.

Pour déterminer la liste des comptes concernés, j’ai dû réaliser un petit script vbs :

On Error Resume Next

Set objConnection = CreateObject("ADODB.Connection")
Set objCommand = CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand.ActiveConnection = objConnection

objCommand.Properties("Page Size") = 1000

objCommand.CommandText = _
    "<LDAP://dc=domain,dc=com>;" & _
    "(&(objectCategory=User)(userAccountControl:1.2.840.113556.1.4.803:=0));" & _
            "Name,homeDirectory;Subtree"
Set objRecordSet = objCommand.Execute

objRecordSet.MoveFirst

Do Until objRecordSet.EOF
username=objRecordSet.Fields("Name").Value
homedir=objRecordSet.Fields("homeDirectory").Value
Wscript.Echo username & ";" & homedir
    objRecordSet.MoveNext
Loop