Le blog technique

Toutes les astuces #tech des collaborateurs de PI Services.

#openblogPI

Retrouvez les articles à la une

[Powershell] Ecrire dans un Excel distant.

Exécuter Excel sur un poste Distant.

Quel est l’interêt ?

En dehors du fait qu’installer Excel sur un serveur n’est pas forcément une bonne pratique, dans mon cas cela me permet d’automatiser à 100% mes scripts Powershell, ces derniers fournissent des rapports détaillés à mes interlocuteurs, sans que je n’ai besoin de faire quelques modifications que ce soit (contrairement à un CSV).
Ainsi les scripts tournent de nuit et permettent à chacun de trouver son rapport le matin en arrivant, et ce même pendant mes vacances.

Comment ça marche

Pour exécuter Excel sur un poste distant via Powershell il faudra cumuler 2 paramètres.

  1. Activer CredSSP pour la délégation des identifiants.
  2. Autoriser le compte d’exécution à se servir de l’application Excel à Distance.

Voyons comment réaliser cela.

1- Activer CredSSP

J’ai déjà traité le sujet ICI.

2- Gestion Excel Distant

Attention la modification que nous allons réaliser n’est pas sans conséquence, comme nous pourrons le voir après, l’application Excel devient vraiment limitée pour tout autre utilisateur que celui qui sera déclaré.

Pour pouvoir autoriser le compte d’exécution à se servir de l’application Excel à distance, nous aurons besoin de la MMC.

Sur la machine qui traitera le fichier Excel :

  • Ouvrir la MMC > Ajouter ou supprimer des composants logiciels enfichables > Services de composants.

  • Développer Services de composants > Ordinateurs > Poste de travail > Configuration DCOM

  • Sur « Microsoft Excel Application » faites clic droit « propriétés », dans la fenêtre sélectionnez l’onglet « Identité »

  • Cochez la case « Cet utilisateur » puis faites « parcourir », sélectionnez « Tout l’annuaire » et enfin recherchez le compte d’exécution du script.

  • Entrez le mot de passe de votre compte d’exécution et faites « Appliquer ».

Maintenant que le paramètre est fixé seul le compte définit est « autorisé » à se servir de l’application Microsoft Excel, l’expérience pour tout autre utilisateur est vraiment dégradé (pas d’impression, de sauvegarde, message d’erreur…); par conséquent si vous souhaitez pouvoir continuer à utiliser Excel, je vous invite à repasser sur « L’utilisateur exécutant » et n’activer la fonction que lors de l’exécution du script (pour ma part je ne l’active sur mon poste que la nuit).

Exemple de message d’erreur.

 

Annexe

En annexe un petit article sur la gestion d’Excel en Powershell :

https://blog.piservices.fr/post/2013/03/27/Powershell-Creation-de28099objets-personnalises

 

Exchange 2013 – Erreur HttpEvent 15021

Problématique

Sur un serveur Exchange 2013, la connexion PowerShell ne s’effectue pas et le portail d’administration affiche une page blanche.

2018-03-29_105153

2018-03-29_105411

Dans le journal d’événements système un grand nombre d’erreur HttpEvent 15021 est présent.

2018-03-29_105128

Ces erreurs peuvent intervenir après le changement d’un certificat sur le serveur Exchange.

Résolution

Le problème vient de la liaison du certificat qui est incorrect sur le port 444. Il faut donc la supprimer et la recréer.

Pour cela, afficher l’ensemble des liaisons SSL avec la commande :

netsh http show sslcert

2018-03-29_105203

Récupérer les valeurs du hash et l’application ID de la liaison sur le port 443.

Supprimer la liaison du port 444 avec la commande :

netsh http delete sslcert ipport=0.0.0.0:444

2018-03-29_105239

Et recréer une liaison en utilisant le hash et l’application ID du port 443 avec la commande :

netsh http add sslcert ipport=0.0.0.0:444 certhash=XXXXXXX appid=”{XXXXXXXX}”

2018-03-29_105352

Dans le journal d’événements deux avertissements HttpEvent 15300 et 15301 apparaissent.

2018-03-29_105518

Une fois la modification effectuée, la connexion PowerShell et le portail d’administration sont de nouveau accessibles.

Supervision – SQL – 3 scripts pour la supervision de Always On

 

Dans le cadre d’un portage de règle de supervision, les trois scripts ci-dessous ont été crées pour remonter l’état de:

– Un Availability Group

– Un Availability Replica

– Un Database Replica

 

En pratique ils utilisent a peu près la même requête SQL. Ils contiennent des éléments propre a l’api scom mais peuvent bien sur être adapté pour être utilisé indépendamment.

Ci-dessous les 3 scripts et l’exemple du code du premier, Check_SQL_AO_AvailabilityGroupStatus_Query_Version.ps1.

 

 

#######################################################################################
#         
#
         
#          Script: SQLAlwaysOnAvailGroupStatus.ps1

#          Purpose: Shows AlwaysOn Availability Group Status
#         
#          Parameters:

#           $DBServer: ShortName of DB Server
#          $InstanceFullName: Name of SQL Instance
#          $AvailGroupName: Availability Group Name
#         
#

########################################################################################

param ( 
           
$Arguments,

           
[string]$DBServer,
           
[string]$InstanceFullName,
       
[string]$AvailGroupName
           
                          
           
)

# Create local variables from override value
.([Scriptblock]::Create($Arguments))

$Scriptname = « SQLAlwaysOnAvailabilityReplicaStatus.ps1 »

# Name of Instance
$ServerInstance = $InstanceFullName.Split(‘\’)[1]

    #Determine TcpPort Used for Instance
    try
    {
    $TcpPort = Get-ItemProperty « HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL11.$ServerInstance\MSSQLServer\SuperSocketNetLib\Tcp\IpAll » | select TcpPort -exp tcpport
    }
    catch
    {
    $Message = « Error during Retrieve of TCP Port Used – Check the execution of the script »
    write-host -f Yellow $Message
    $PropertyBag.AddValue(« State », »WARNING »)
    $PropertyBag.AddValue(« Message »,$Message)
    $PropertyBag.AddValue(« ReplicaName »,$AvailabilityReplicaName)
    $PropertyBag
    Exit 1
    }

# Scom Object and Property Bag
$api = New-Object -comObject “MOM.ScriptAPI” 
$PropertyBag = $api.CreatePropertyBag()

# Function  GetAODBRepStatus
Function GetAOAvailGroupStatus
                        {
                        param(
                                    [string]$TargetComputer=$DBServer,
                                    $global:Source = « $DBServer\$ServerInstance »,
                                    [string]$sqlCommand =
                                            $(« 
                                            SELECT
                                           
                                            AO_AG.name as AvailGroupName
                                            ,HADR_AO_AGS.synchronization_health as AvailGroup_SyncHealth
                                            ,HADR_AO_AGS.synchronization_health_desc as AvailGroup_SyncHealth_Desc
                                            –,SYSDB.name as DBName
                                           
                                            –,AO_AVREP.replica_server_name
                                            –,HADR_AVAIL_REP_STATE.synchronization_health as AvailReplica_SyncHealth
                                           
                                            –,HADR_AVAIL_REP_STATE.synchronization_health_desc as AvailReplica_SyncHealth_Desc

                                            –,HADR_DB_REP_STATE.synchronization_state as DBReplica_SyncState
                                            –,HADR_DB_REP_STATE.synchronization_state_desc as DBReplica_SyncState_Desc
                                            –,HADR_DB_REP_STATE.synchronization_health_desc as DBReplica_SyncHealth_Desc

                                           
                                            FROM
                                            [sys].[availability_databases_cluster] AO_DB_CLUS
                                            –INNER JOIN sys.databases SYSDB on CAST(SYSDB.group_database_id AS VARCHAR(50)) =  CAST(AO_DB_CLUS.group_database_id AS VARCHAR(50))
                                            INNER JOIN sys.dm_hadr_database_replica_states HADR_DB_REP_STATE on CAST(HADR_DB_REP_STATE.group_database_id AS VARCHAR(50)) = CAST(AO_DB_CLUS.group_database_id AS VARCHAR(50))
                                            –INNER JOIN sys.dm_hadr_availability_replica_states HADR_AVAIL_REP_STATE on HADR_AVAIL_REP_STATE.group_id = HADR_DB_REP_STATE.group_id
                                            INNER JOIN sys.availability_groups AO_AG on AO_AG.group_id = HADR_DB_REP_STATE.group_id
                                            INNER JOIN sys.dm_hadr_availability_group_states HADR_AO_AGS on HADR_AO_AGS.group_id = AO_AG.group_id
                                            –INNER JOIN [sys].[availability_replicas] AO_AVREP on AO_AVREP.replica_id = HADR_DB_REP_STATE.replica_id
                                            WHERE AO_AG.name = ‘
$AvailGroupName

                                           
                                            GROUP BY
                                            AO_AG.name
                                            ,HADR_AO_AGS.synchronization_health
                                            ,HADR_AO_AGS.synchronization_health_desc
                                            « 
                                            )
                               )

                               Try
                               {

                                                                                           
                                               

                                            $global:connectionString = « Data Source=$Source,$TcpPort; » +
                                            « Integrated Security=SSPI;  » +
                                            « Initial Catalog=master »

                                           
                                            $connection = new-object system.data.SqlClient.SQLConnection($connectionString)
                                           
                               
                                                               
                                            $connection.Open()
                                                                                 
                                                                              

                                            $command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)

                                }
                                catch
                                {
                                write-host -F Red $(« Error during sql connection – check the credentials used »).ToUpper()
                                #exit 1
                                }

                                $adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
                                $set = New-Object System.Data.DataSet
                                $adapter.Fill($Set) | Out-Null

                                $connection.Close()
                                $Set.Tables

                               

                              }

# Execute function and get data
try
{
[array]$AvailGroup = GetAOAvailGroupStatus
}
catch
{
$Message = « WARNING – Error during Connection to master database or execution of sql query »
write-host -F Yellow $Message
$PropertyBag.AddValue(« State », »WARNING »)
$PropertyBag.AddValue(« Message »,$Message)
$PropertyBag.AddValue(« DBServer »,$DBServer)
$PropertyBag.AddValue(« connectstring »,$connectionString)
$PropertyBag.AddValue(« AvailGroupName »,$AvailGroupName)
$PropertyBag.AddValue(« AvailGroupStatus », »no_data »)
$PropertyBag
exit 1
}

if (!($AvailGroup))
{
$Message = « WARNING – Error – No Availability Group have been found »
write-host -F Yellow $Message
$PropertyBag.AddValue(« State », »WARNING »)
$PropertyBag.AddValue(« Message »,$Message)
$PropertyBag.AddValue(« DBServer »,$DBServer)
$PropertyBag.AddValue(« AvailGroupName », »no_data »)
$PropertyBag.AddValue(« AvailGroupStatus », »no_data »)
$PropertyBag
exit 1
}

 

try
{
$AvailGroupState = $AvailGroup  | select AvailGroup_SyncHealth_Desc -ExpandProperty AvailGroup_SyncHealth_Desc
}
catch
{
$Message = « Error during Retrieve of Availability Group State »
Write-Host -ForegroundColor Yellow $Message
$PropertyBag.AddValue(« State », »WARNING »)
$PropertyBag.AddValue(« Message »,$Message)
$PropertyBag.AddValue(« DBServer »,$DBServer)
$PropertyBag.AddValue(« AvailGroupName »,$AvailGroupName)
$PropertyBag.AddValue(« AvailGroupStatus », »no_data »)
$PropertyBag
exit 1
}

« AVAILABILITY GROUP: $AvailGroupName »

If ($AvailGroupState -eq « Healthy »)

        {
        $Message = « OK – Status of $AvailGroupName Availability Group is Healthy »
        write-host -f Green $Message
        $PropertyBag.AddValue(« State », »OK »)
        $PropertyBag.AddValue(« Message »,$Message)
        $PropertyBag.AddValue(« DBServer »,$DBServer)
        $PropertyBag.AddValue(« AvailGroupName »,$AvailGroupName)
        $PropertyBag.AddValue(« AvailGroupStatus », »Healthy »)
        $PropertyBag
        Exit 0
        }
       

ElseIf ($AvailGroupState -eq « Error »)

   
        {
        $Message = « CRITICAL – Status of $AvailGroupName Availability Group is Error »
        write-host -f Red $Message
        $PropertyBag.AddValue(« State », »CRITICAL »)
        $PropertyBag.AddValue(« Message »,$Message)
      $PropertyBag.AddValue(« DBServer »,$DBServer)
        $PropertyBag.AddValue(« AvailGroupName »,$AvailGroupName)
        $PropertyBag.AddValue(« AvailGroupStatus », »Error »)
        $PropertyBag
        Exit 0
        }
       
Else   {
        $Message = « WARNING – Status of $AvailGroupName Availability Group cannot be determined »
        write-host -f yellow $Message
        $PropertyBag.AddValue(« State », »WARNING »)
        $PropertyBag.AddValue(« Message »,$Message)
      $PropertyBag.AddValue(« DBServer »,$DBServer)
        $PropertyBag.AddValue(« AvailGroupName »,$AvailGroupName)
        $PropertyBag.AddValue(« AvailGroupStatus », »no_data »)
        $PropertyBag
        Exit 1
        }
 
 

########################################################################################