dimanche 26 avril 2009

Formation PowerShell #04 : Exchange Management Shell

Voici la quatrième session de la formation PowerShell en vidéo !
Cette session est un peu spéciale puisqu'elle est dédiée à Exchange Management Shell.

Exchange 2007 étant basé sur PowerShell, nous aborderons dans cette vidéo l'administration de ce serveur de messagerie en ligne de commande.


Vous pouvez retrouver l'ensemble des sessions ICI !

mercredi 22 avril 2009

Configuration du portail : Active Directory et SQL

La partie précédente de cet article se trouve ICI !

Nous arrivons à la dernière phase du déploiement de notre portail Web : la configuration !

Biensûr, la configuration d'un portail dépend du portail lui-même mais aussi de l'environnement dans lequel il est déployé. Le sujet ici est simplement de voir certaines petites astuces en PowerShell autour d'Active Directory et de SQL !

En effet, notre portail Web a besoin d'une unité organisationnelle (OU) dans notre Active Directory afin d'y rassembler les groupes qu'il utilise (par exemple le groupe des Administrateurs du portail !) ou encore d'exécuter certaines procédures stockées de notre base de données !

Bref nous allons nous voir tout celà en détail !

La première chose est de créer notre unité organisationnelle dédiée à notre portail :

### <summary>
### Creates Web portal prerequires in Active Directory.
### </summary>
### <return>True on success, false otherwise</return>
function Configure-ADWebPortal()
{
    trap {
        Display-Info $_.Exception.Message
        $bln = $false
        continue
    }
    $bln = $true
    $CONST_GLOBAL_GRP = 2
    $CONST_UNIV_GRP = 8
    Display-Info 'Creating WebPortal Organizational Unit...'
    $objOU = [ADSI]''
    $objOU = $objOU.Create('OrganizationalUnit', 'OU=WebPortal')
    $objOU.SetInfo()
    
    Create-Group $objOU 'AdmWebPortal' 'WebPortal Administrators' $CONST_UNIV_GRP
    
    return $bln
}

La fonction Configure-ADWebPortal crée notre OU et un groupe dans celle-ci. Si vous avez suivi les formations PowerShell en vidéo, vous ne découvrez rien de neuf !

Remarquez cependant qu'ici nous avons fait une petite fonction permettant de créer un groupe Active Directory dont voici le code :

### <summary>
### Creates an Active Directory group.
### </summary>
### <param name ="objOU">The container (ADSI object)</param>
### <param name ="strName">Group's name</param>
### <param name ="strDescription">Group's description</param>
### <param name ="intGroupType">Group's type</param>
function Create-Group($objOU, $strName, $strDescription, $intGroupType)
{
    trap {
        Display-Error $_.Exception.Message
        continue
    }
    Display-Info "Creating group $strName..."
    $objGroup = $objOU.Create('Group', "CN=$strName")
    $objGroup.Put('SamAccountName', "$strName")
    $objGroup.Put('Description', "$strDescription")
    $objGroup.Put('GroupType', $intGroupType)
    $objGroup.SetInfo()
}

Afin de pouvoir tracer d'éventuels problèmes, nous ajoutons des appels à nos fonctions affichant et loggant les informations.

Dans notre exemple, le groupe créé est Universel. Pourquoi ? Peut-être que nous en ferons une liste de diffusion sous Exchange 2007 (très) prochainement...

C'est très bien, nous avons réussi à créer des objets Active Directory, à exécuter des scripts SQL... et maintenant ?

Nous allons modifier en masse et à la volée notre Active Directory et notre base de données ! En effet, le but de la prochaine fonction va être simple :
  • Récupération de toutes les OU dont le nom est normé d'une certaine manière
  • Création d'un groupe dans chacune de ces OU
  • Insertion du nom de l'OU traitée dans notre base de données en exécutant une procédure stockée !

### <summary>
### Synchronizes SQL database and Active Directory.
### </summary>
### <param name="objSQL">SQL connection object</param>
### <return>True on success, false otherwise</return>
function Synchronize-ADAndSQL($objSQL)
{
    trap {
        Display-Error $_.Exception.Message
        $bln = $false
        continue
    }
    $CST_GLB_GRP = 2
    $bln = $true
    $objEntry = [ADSI]''
    Clean-Database $objSQL
    foreach ($obj in $objEntry.psbase.Children) {
        if ($obj.Name.Value -match "^(\w+\d+)_(\w+)$") {
            $strFullName = $matches[0]
            $strCode = $matches[1]
            $strName = $matches[2]
            $strDN = $obj.psbase.Properties.DistinguishedName.Value
            Display-Info "OU $strFullName discovered"
            $strGroupName = "$strCode" + '_Administrators_WebPortal'
            Create-Group $obj "$strGroupName" "Admins for $strCode" $CST_GLB_GRP

            $objAdapter = New-Object Data.SqlClient.SqlDataAdapter
            $objAdapter.SelectCommand = New-Object Data.SqlClient.SqlCommand
            $objAdapter.SelectCommand.Connection = $objSQL
            $objAdapter.SelectCommand.CommandText = '[PR_ADDOU]'
            $objAdapter.SelectCommand.CommandType =
                [Data.CommandType]::StoredProcedure

            $objSqlParam = Build-SQLParam '@OUCode' $strCode
            [void]$objAdapter.SelectCommand.Parameters.Add($objSqlParam)
            $objSqlParam = Build-SQLParam '@OUName' $strName
            [void]$objAdapter.SelectCommand.Parameters.Add($objSqlParam)
            $objSqlParam = Build-SQLParam '@OUDN' $strDN

            $objAdapter.SelectCommand.ExecuteNonQuery() | Out-Null
        }
    }
    
    return $bln
}

Le nom des OU est formatté en deux parties : un code et le nom de l'OU séparés par le caractère '_'. Notre procédure stockée nommée PR_ADDOU prend en paramère ces informations et est exécutée : un bon moyen de séparer les traitements ; si le traitment côté SQL est modifié, le script PowerShell ne l'est pas !

Ce petit Solution Accelerator de déploiement d'un portail Web n'est biensûr qu'un exemple mais grâce à lui, nous avons vu un cas pratique d'automatisation !

jeudi 16 avril 2009

Automatisation de IIS

La partie précédente de cet article se trouve ICI !

Après avoir déployé notre base de données sur laquelle s'appuiera notre portail Web, nous allons nous occuper du site Web.

En utilisant le deuxième menu...


... Nous découvrons notre interface graphique nous permettant de déployer notre site :


Là encore, l'interface est minimaliste. Les informations demandées sont :
  • Nom du serveur Web sur lequel déployer notre site,
  • Le port d'écoute,
  • Le chemin contenant les sources de notre site Web,
  • Un identifiant et un mot de passe qui seront utilisés pour le pool d'application.
Biensûr, les trois premiers champs sont pré-remplis.

Intéressons-nous au code PowerShell !

La première chose à faire est de vérifier que les informations renseignées sont correctes :

### <summary>
### Checks the Web part parameters.
### </summary>
### <return>True if parameters are correct, false otherwise</return>
function Check-WebParameters()
{
    $bln = $false
        
    $bln = ($TxtWebServer.Text.Length -gt 0-and
           ($TxtWebPort.Text.Length -gt 0-and
           ($TxtWebPath.Text.Length -gt 0-and
           ($TxtWebLogin.Text.Length -gt 0-and
           ($TxtWebPassword.Text.Length -gt 0)
    if ($bln) {
        $bln = ($TxtWebServer.Text -match "^\w[\w\d-_]*$"-or
               ($TxtWebServer.Text -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
        if ($bln -eq $false) {
            Display-error 'Specified server name is invalid'
        } else {
            $bln = ($TxtWebPort.Text -match "^\d{1,5}$")
            if ($bln -eq $false) {
                Display-error 'Specified port is invalid'
            } else {
                $bln = Test-Path $TxtWebPath.Text
                if ($bln -eq $false) {
                    Display-error 'Specified Web folder is invalid'
                } else {
                    $strLogin = $TxtWebLogin.Text
                    if ($strLogin.Contains('\')) {
                        $strLogin = $strLogin.Split('\')[1]
                    }
                    $objUser = SearchOne-InDomain '' "(samaccountname=$strLogin)"
                    if ($objUser -eq $null) {
                        Display-error 'Specified user doesn''t exist in the domain'
                        $bln = $false
                    }
                }
            }
        }
    } else {
        Display-error 'All fields are required'
    }
    
    return $bln
}

La fonction Check-WebParameters vérifie :
  • La validité du nom du serveur ou de l'adresse IP renseignée grâce à une expression régulière,
  • La validité du port (un nombre de 5 caractères maximum)
  • L'existence du compte utilisateur Active Directory renseigné. Cette dernière vérification est faite grâce à SearchOne-InDomain qui est une petite fonction dont voici le code :

    ### <summary>
    ### Searchs one object in an Active Directory container.
    ### </summary>
    ### <param name ="domainName">The container</param>
    ### <param name ="filter">LDAP filter</param>
    ### <param name ="scope">The scope for the research (optional)</param>
    ### <returns>Active Directory object</returns>
    function SearchOne-InDomain($container, $filter,
                                $scope = [DirectoryServices.SearchScope]::Subtree)
    {
        trap { return $null }
        $entry = New-Object DirectoryServices.DirectoryEntry("$container")
        $searcher = New-Object DirectoryServices.DirectorySearcher($entry)

        $searcher.Filter = $filter
        $searcher.SearchScope = $scope
        $searcher.FindOne()
    }

    Du déjà vu !
Bref une fois que nous avons vérifié que les informations renseignées sont correctes nous pouvons déployer notre site Web !

### <summary>
### Installs the IIS website.
### </summary>
function Deploy-WebServer()
{
    $bln = Check-WebParameters
    
    if ($bln -eq $false) { return }
    $bln = Copy-Source
    if ($bln -eq $false) { return }
    $LblError.Text = ''
    $strServer = $TxtWebServer.Text
    $strPath = $TxtWebPath.Text
    $strWebSiteName = 'Provisioning'
    $objLocator = New-Object -com WbemScripting.SWbemLocator
    $objProvider = $objLocator.ConnectServer($strServer, 'root/MicrosoftIISv2')
    $objService = $objProvider.Get("IIsWebService='W3SVC'")
    $objBindings = @($objProvider.Get('ServerBinding').SpawnInstance_())
    $objBindings[0].Properties_.Item('Port').value = $TxtWebPort.Text
    $createNewSiteMethod = $objService.Methods_.Item('CreateNewSite')

    $objInParameters = $createNewSiteMethod.InParameters.SpawnInstance_()
    $objInParameters.Properties_.Item('PathOfRootVirtualDir').value = $strPath
    $objInParameters.Properties_.Item('ServerBindings').value = $objBindings
    $objInParameters.Properties_.Item('ServerComment').value = $strWebSiteName
    
    Display-Info "Creating new WebSite '$strWebSiteName'..."
    $objOutParameters = $objService.ExecMethod_("CreateNewSite", $objInParameters)
    Display-Info "WebSite '$strWebSiteName' created"
    $id = ''
    $objOutParameters.properties_ | % {
        $id = $_.Value -match "[^']'([^']+)'.*"
        if ($id) { $id = $matches[1] }
    }
    if ($id.ToUpper() -match "^W3SVC/\d+$") {
        Display-Info "Creating new Application Pool '$strWebSiteName'..."
        $bln = Create-ApplicationPool $strServer $strWebSiteName
        if ($bln) {
            Display-Info "Configuring Website '$strWebSiteName'"
            $objSite = [ADSI]"IIS://$strServer/$id/Root"
            $objSite.Put("DefaultDoc", "Default.aspx")
            $objSite.Put("AppPoolId", $strWebSiteName)
            $objsite.put("AuthFlags", 4)
            $objsite.Put("AppFriendlyName", $strWebSiteName)
            $objsite.Put("AccessFlags", 1)
            $objsite.Put("AccessRead", $true)
            $objsite.Put("AccessScript", $true)
            $objsite.Put("AccessExecute", $true)
            $objSite.SetInfo()
            if ((Get-ChildItem env:COMPUTERNAME).Value -eq $strServer) {
                Set-FrameWorkVersion $id
                Display-Info "WebSite '$strWebSiteName' successfully created"
            } else {
                Display-Info "WebSite '$strWebSiteName' successfully created. " +
                             "You need to manually set up the .Net 2 version"
            }
        } else {
            Display-Error "Error creating Application pool '$strWebSiteName'"
            Display-Info "WebSite '$strWebSiteName' has not been configured"
        }
    } else {
        Display-Error "Invalid WebSite ID ($id)"
        Display-Info "WebSite '$strWebSiteName' has not been configured"
    }
}

La fonction Deploy-WebSite fait le travail ! Nous allons y jeter un petit coup d'oeil car c'est le plus intéressant !

En début de fonction, nous utilisons Copy-Source : c'est une petite fonction qui utilise la Cmdlet Copy-Item pour copier les sources de notre portail vers le dossier de notre futur site ; rien d'exceptionnel, alors nous continuons !

Ensuite, vous pouvez remarquez que nous attaquons notre serveur IIS en ADSI. Celà rend cette fonction utilisable à partir de la version 6 de IIS.

Dans cette fonction nous faisons donc les opérations suivantes :
  • Création du site Web avec un nom et un port d'écoute donné,
  • Création d'un pool d'application qu'utilisera notre site. Pour celà nous avons créé la fonction Create-ApplicationPool :

    ### <summary>
    ### Creates the IIS application pool.
    ### </summary>
    function Create-ApplicationPool($strServer, $strAppPoolName)
    {
        trap [Exception] {
            Display-Info $_.Exception.Message
            $bln = $false
            continue
        }
        $bln = $false
        $objApp = [ADSI]"IIS://$strServer/W3SVC/AppPools/$strAppPoolName"
        if ($objApp.distinguishedname -eq $null) {
            $strDomain = ([ADSI]'').Name.Value
            $objApp = [ADSI]"IIS://$strServer/W3SVC/AppPools"
            $objPool = $objapp.Create("IIsApplicationPool", $strAppPoolName)
            $objPool.Put('AppPoolIdentityType', 3)
            $strLogin = $TxtWebLogin.Text
            if ($strLogin.Contains('\'-eq $false) {
                $TxtWebLogin.Text = "$strDomain\$strLogin"
                $TxtWebLogin.Refresh()
            } else {
                $strLogin = $strLogin.Split('\')[1]
            }
            Display-Info "Setting Application pool credentials to $strLogin..."
            $objPool.Put('WAMUserName', $TxtWebLogin.Text)
            $objPool.Put('WAMUserPass', $TxtWebPassword.Text)
            $objPool.SetInfo()
            Display-Info "Adding user $strLogin to group IIS_WPG..."
            $objGroup = [ADSI]"WinNT://$strServer/IIS_WPG"
            $objGroup.Add("WinNT://$strDomain/$strLogin")
            $bln = $true
        } else {
            Display-Info "Application pool '$strAppPoolName' already exists."
            $bln = $true
        }
        
        return $bln
    }

    Cette fonction ajoute également le compte utilisé par le pool d'application dans le groupe local IIS_WPG.
  • Configuration du site Web :
    • Page par défaut paramétrée à la page Default.aspx
    • Utilisation du pool d'application précédemment créé
    • Paramétrage de l'authentification Windows
    • Mise en place des droits de lecture et d'exécution
  • Utilisation de la version 2 du framework .NET. Cette étape n'est faite que si notre Solution Accelerator est lancé depuis le serveur Web. La fonction Set-FrameworkVersion configure notre site web pour qu'il utilise la version 2 :

    ### <summary>
    ### Sets the .NET framework version for a given website.
    ### </summary>
    ### <param name="strID">Website ID</param>
    function Set-FrameWorkVersion($strID)
    {
        $strPath = (Get-ChildItem Env:windir).Value + '\Microsoft.NET\Framework\'
        
        $objDir = Get-ChildItem $strPath | where {
                   ($_.GetType().ToString() -eq 'system.io.directoryinfo'-And
                   ($_.name -match "^(v2.[\d\.]+)$")} | sort name
        if ($objDir -ne $null) {
            $strPath += $matches[1]
            $strPath += "\aspnet_regiis.exe"
            $objSI = New-Object System.Diagnostics.ProcessStartInfo
            $objSI.UseShellExecute = $false
            $objSI.FileName = $strPath
            $objSI.Arguments = "-s $strID/Root"
            $objSI.RedirectStandardOutput = $true
            $objP = [System.Diagnostics.Process]::Start($objSI)
            $objP.WaitForExit()
        }
    }

    Vous pouvez remarquer que nous exécutons le binaire aspnet_regiis.exe !
Biensûr, les erreurs sont affichées en bas de notre fenêtre si elles surviennent !

Nous arrivons à la fin de l'automatisation de IIS... J'espère que ça n'a pas été trop long !

mercredi 8 avril 2009

Déploiement de la base de données

La partie précédente de cet article se trouve ICI.

Avant de déployer notre site Web, nous devons installer la base de données : quel portail Web ne possède pas de base de données aujourd'hui ? :)

Pour celà, notre Solution Accelerator va faire le travail pour nous. Dans l'article précédent, je vous ai présenté l'interface graphique : nous allons donc l'utiliser !

Nous allons cliquer sur notre premier menu afin de pouvoir déployer notre base de données !

Nous découvrons donc l'interface graphique dédiée :

Elle est volontairement sobre !
En effet, nous n'avons qu'à saisir le nom du serveur SQL ainsi que le nom de l'instance et cliquer sur OK...

Dans cette interface, nous avons tout de même prévu de pouvoir spécifier le compte avec lequel faire ce déploiement : visible tout en haut de la fenêtre. En effet, nous avons la possibilité d'utiliser les informations de connexion de l'utilisateur courant ou bien d'en spécifier d'autres.

Vous pouvez remarquer qu'en bas de la fenêtre il y a un grand espace vide... Nous n'allons pas le laisser vide ! En effet, cette zone est en fait réservée à l'affichage des informations et des erreurs !
Chaque étape du script est décrite dans cette zone ainsi que les éventuelles erreurs : ce sont les fonctions Display-Info ou Display-Error qui s'en chargeront :

### <summary>
### Adds a log.
### </summary>
### <param name="str">Message to add</param>
### <param name="strType">Message type</param>
function Log-Message($str, $strType)
{
    $strDate = [DateTime]::Now.ToString('G')
    $strType = $strType.ToUpper()
    $strLine = "[$strDate] $strType: $str"
    Write-Verbose $strLine
    $script:strLog += $strLine + "`r`n"
}

### <summary>
### Displays error message.
### </summary>
### <param name="str">Message to display</param>
function Display-Error($str)
{
    $LblInfo.Text = ''
    $LblError.Text = $str
    $LblError.Refresh()
    Log-Message $str 'Error'
}

### <summary>
### Display information message.
### </summary>
### <param name="str">Message to display</param>
function Display-Info($str)
{
    $LblInfo.Text = $str
    $LblInfo.Refresh()
    Log-Message $str 'Info'
}

Biensûr ces informations et erreurs sont enregistrées dans nos logs, d'où la fonction Log-Message qui ne fait qu'ajouter une date de génération du message (information ou erreur) !

Bref vous l'aurez compris, la traçabilité est importante et doit être présente dans nos scripts !

Maintenant que la présentation de l'interface est faite, nous pouvons passer à la partie la plus interessante : le traitement fait en PowerShell lorsqu'on appuie sur le bouton !

Avant tout, nous devons vérifier que nous avons tous les pré-requis nécessaires pour faire notre déploiement : les scripts SQL que nous exécuterons !

Pour celà, nous allons déclarer quelques variables qui seront visibles dans tout notre script :

### <summary>
### Path to current script.
### </summary>
$script:strPath = $MyInvocation.MyCommand.Path

### <summary>
### SQL scripts to execute during installation step.
### Order is essential.
### </summary>
$script:arrSQLGenerateDBScripts = 'CreateDatabase.sql',
                                  'ProvisioningDatabase.sql'
### <summary>
### Installation logs.
### </summary>
$script:strLog = ''

On peut remarquer ici l'utilisation de la variable $MyInvocation très utile puisqu'elle nous donne des informations sur notre dernière commande : ici on récupère le chemin où se trouve notre script PowerShell !

La variable arrSQLGenerateDBScripts contient une liste ordonnée de nos scripts SQL.

Enfin, strLog est une simple chaîne de caractères qui fera office de log (il faut garder une trace des actions et des erreurs !).

Nous allons dans un premier temps vérifier que les fichiers sont présents :

### <summary>
### Checks SQL files.
### </summary>
### <return>True on success, false otherwise</return>
function Check-SQLFiles()
{
    trap {
        $blnRes = $false
        continue
    }
    $tab = $script:strPath.Split('\')
    $strPath = ''
    for ($i = 0$i -lt $tab.length - 2$i++) {
        $strPath += $tab[$i+ '\'
    }
    $blnRes = $true
    $bln = $true
    Display-Info "Checking files in $strPath..."
    if ($script:arrSQLGenerateDBScripts.Length -gt 0) {
        foreach ($strFile in $script:arrSQLGenerateDBScripts) {
            $bln = Test-Path "$strPath\Database\$strFile"
            if ($bln -eq $false) { break }
        }
        $blnRes = $blnRes -and $bln
    }
    if ($blnRes) {
        Display-Info "Installation files successfully checked"
    } else {
        Display-Error "Installation files missing."
    }
    
    return $blnRes
}

Cette fonction vérifie que nos scripts SQL contenus dans la variable de script existent dans le répertoire ../Database.

Maintenant que nous sommes certains de la présence de nos scripts à exécuter nous allons... les exécuter ! :)
Pour celà, nous devons biensûr nous connecter à notre base de données grâce à une chaîne de connexion :

### <summary>
### Generate the SQL connection string.
### </summary>
### <param name="strDB">Database name</param>
### <return>SQL connection string</return>
function Generate-StringConnection($strDB)
{
    $strInstance = $TxtDBServer.Text
    if ($TxtDBInstance.Text.Length -gt 0) {
        $strInstance += '\' + $TxtDBInstance.Text
    }
    $strInstance = $strInstance.ToUpper()
    $objBuilder = New-Object System.Data.SqlClient.SqlConnectionStringBuilder
    $objBuilder['Application Name'= 'Web Portal'
    $objBuilder['Data Source'= $strInstance
    $objBuilder['Initial Catalog'= $strDB

    if ($ChkWindowsAuth.Checked -eq $false) {
        $objBuilder['User Id'= $TxtLogin.Text
        $objBuilder['Password'= $TxtPassword.Text
    } else {
        $objBuilder['Integrated Security'= $true
    }

    return $objBuilder.ConnectionString
}

Cette fonction prend en paramètre le nom de la base de données qui sera affecté au paramètre "Initial Catalog" de notre chaîne de connexion.
Vous pouvez remarquer que si aucun nom d'instance n'est renseigné dans l'interface graphique, nous utilisons le nom du serveur (instance par défaut !).

Ensuite, nous pouvons écrire notre fonction qui va exécuter un script SQL :

### <summary>
### Executes a SQL script.
### </summary>
### <param name="strScript">Script full name</param>
### <param name="objSQL">SQL connection</param>
### <return>True on success, false otherwise</return>
function Execute-SQLScript($objSQL, $strScript)
{
    trap {
        Display-Error $_.Exception.Message
        $bln = $false
        continue
    }
    $bln = Test-Path $strScript
    if ($bln) {
        Display-Info "Executing script $strScript..."
        $objSQLCmd = New-Object System.Data.SqlClient.SqlCommand
        $objSQLCmd.Connection = $objSQL
        $strContent = ''
        foreach ($strLine in Get-Content $strScript) {
            if ($bln -eq $false) { return $bln }
            if ($strLine.ToUpper() -match "^\s*GO\s*$") {
                $objSQLCmd.CommandText = $strContent
                $objSQLCmd.ExecuteNonQuery() | Out-Null
                $strContent = ''
            } else {
                $strContent += $strLine + "`r`n"
            }
        }
    } else {
        $LblError.Text = "File $strScript not found"
    }
    
    return $bln
}

Il y a plusieurs méthodes pour exécuter un script. Ici nous avons choisi de lire le fichier SQL ligne par ligne et exécuter le résultat de notre lecture dès que nous rencontrons l'instruction SQL "GO".

C'est l'occasion de voir une expression régulière !
Rapidement, "^\s*GO\s*$" signifie que nous cherchons à identifier une chaîne de caractères contenant le mot GO entouré ou non d'espaces !

Comme il est toujours intéressant de savoir si notre script s'est bien exécuté, cette fonction retourne un booléen nous donnant cette information :)

Bref, nous avons tous les élements en main pour déployer notre base de données :

### <summary>
### Installs the SQL database.
### </summary>
function Install-Database()
{
    trap {
        Display-Error $_.Exception.Message
        if (($objSQL -ne $null-and
            ($objSQL.State -eq [Data.ConnectionState]::Open)) {
            $objSQL.Close()
        }
        $bln = $false
        continue
    }
    $LblError.Text = ''
    $objSQL = New-Object System.Data.SqlClient.SqlConnection
    $objSQL.ConnectionString = Generate-StringConnection 'master'
    Display-Info "Opening database connection..."
    $objSQL.Open()
    if ($objSQL.State -eq [Data.ConnectionState]::Open) {
        $strPath = Get-SQLScriptPath
        foreach ($strScript in $script:arrSQLGenerateDBScripts) {
            $bln = Execute-SQLScript $objSQL "$strPath\$strScript"
            if ($bln -eq $false) { break }
        }
        $objSQL.Close()
        Display-Info "Database connection closed"
        if ($bln) {
            Display-Info "Database successfully installed"
        } else {
            Display-Error "Error executing script $strPath\$strScript."
            Display-Info "Database installation aborted"
        }
    }
}

Nous sommes à la fin !

Cette fonction se sert donc de ce que nous avons vu avant et deploie notre base de données en plusieurs étapes :
  • Création de la chaîne de connexion à la base de données
  • Ouverture de la connexion à la base de données
  • Exécution des scripts SQL
  • Fermeture de la connexion à la base de données

Tout celà en affichant les étapes et les éventuelles erreurs dans notre fenêtre graphique !

Il ne nous reste plus qu'à déployer notre portail web en automatisant IIS ! Rendez-vous donc au prochain article !

mardi 31 mars 2009

Automatisation du déploiement d'un portail Web

Déployer un portail web n'est pas toujours très simple. Il faut vérifier que tous les pré-requis sont installés, faire le déploiement, vérifier la configuration... et encore, j'en oublie :)

Ce que nous chercherons à faire ici est par contre très simple : déployer un portail Web en appuyant sur un bouton... et tant qu'à faire, le bouton sera joli !

Vous l'aurez compris, c'est une sorte de "Solution Accelerator" (terme à la mode :p) que nous allons créer !

A quoi va ressembler notre interface graphique ? Voyons voir...



L'interface est simple mais efficace ! Elle met en évidence les trois étapes que nous détaillerons dans les articles à venir :
  • Déploiement de la base de données (exécution de script SQL)
  • Déploiement du portail Web (automatisation de IIS)
  • Configuration du portail (interaction avec Active Directory et l'Univers...)
Le décor est planté, il ne reste plus qu'à ouvrir notre session PowerShell !

A très bientôt pour notre première étape !

mercredi 18 mars 2009

Niveau Manage


Nous allons clore notre aperçu des différentes phases constituant le cycle de vie des services de la version 4 de MOF. Une fois ces phases appropriées, j'essaierai de vous présenter les processus intégrés à chacune de ces phases en adoptant une présentation moins théorique. En effet l'objectif de ce blog, outre le fait de vous initier au PowerShell et aux fondamentaux de MOF/ITIL est surtout de vous démontrer l'efficacité de leur association. Pour cela nous (Gregory et moi) vous présenterons des exemples pratiques d'associations de processus MOF et de scripts PowerShell.

Terminons donc notre vue d'ensemble des phases par le niveau Manage.
Bonne lecture.

Le niveau Manage intègre les processus de prise de décisions, de gestion des risques et de gestion du changement qui se produisent tout au long de la durée de vie des services IT. Il contient également les processus liés à la définition des rôles et responsabilités.
Le niveau Manage représente le fondement de toutes les phases du cycle de vie. Le niveau Manage promeut la cohérence dans la planification et la fourniture de services IT et fournit les bases pour le développement et l'exploitation d’un environnement IT résilient.


L'objectif principal du niveau Manage est de mettre en place une approche intégrée des activités de la gestion des services IT. Cette approche permet de coordonner les processus décrits dans les trois phases du cycle de vie: Plan, Deliver, et Operate. Cette coordination est renforcée par:

  • Le développement du processus de prise de décision
  • L’utilisation de la gestion des risques et des contrôles dans le cadre de tous les processus
  • La promotion des processus de changement et de configuration qui sont contrôlés
  • La division du travail afin que les responsabilités soient claires et ne soient pas en conflit


Des conseils spécifiques sont fournis pour accroître la probabilité que:

  • L'investissement informatique fournit la valeur business attendue
  • Les décisions d’investissement et d’allocation des ressources sont associées aux personnes appropriées
  • Il y a un niveau de risque acceptable
  • Des processus contrôlés et documentés sont utilisés
  • Les responsabilités sont communiqués et leur propriétaire mis en évidence
  • Les politiques et les contrôles internes sont efficaces et fiables

La réalisation de ces objectifs est plus susceptible d'être atteinte si l’informatique travaille à:

  • Des structures de gouvernance IT explicites et des processus
  • L'organisation IT et l'organisation des entreprises partagent une approche commune pour la gestion des risques
  • Planification régulières des revues de gestion des politiques et des contrôles internes