PI Services

Le blog des collaborateurs de PI Services

O365 : Forcer le changement de mot de passe

Il arrive dans certain cas de figure que nous souhaitions forcer le changement de mot de passe des comptes O365.

S'il s'agit d'un seul utilisateur voici la commande :

Set-MsolUserPassword -UserPrincipalName jdupont@mondomaine.com -ForceChangePasswordOnly $true -ForceChangePassword $true

Prenez soin de modifier le Userprincipalname de la commande sous peine d'obtenir une erreur.

 

En revanche si l'on veut forcer l'intégralité de la société à changer de mot de passe cela va nécessiter quelques lignes de plus.

# Variables
$LogFolder = "C:\temp"
$LogFile = "$LogFolder\LogO365_Reset.txt"
$LogFileError = "$LogFolder\LogO365_Reset_Error.txt"

# check
If (!(Test-Path $LogFolder)) {
    New-Item $LogFolder -ItemType Directory
    }

# Exceptions
$FilteredUsers = "Userprincipalname1", "Userprincipalname2", "Userprincipalname3" # Ajouter les UPN à ne pas reset (exemple  le compte Admin du Tenant)

# Connect to O365
Connect-MsolService

# Get users
$AllUsers = Get-MsolUser -MaxResults 10000 | select UserPrincipalName

# Force to change Password
$AllUsers | foreach {
    $UPN = $_.UserPrincipalName
    If ($FilteredUsers -eq $UPN) {
        Write-Host "Do not reset Password For $UPN because we had filtered him" | Add-Content $LogFile
        }
    Else {
        Try {
            Set-MsolUserPassword -UserPrincipalName $UPN -ForceChangePasswordOnly $true -ForceChangePassword $true
            Write-Output "Succesfully reset Password For $UPN" | Add-Content $LogFile
            }
        Catch {
            $Date = Get-Date
            Write-Warning "At $Date the following Error Appear $($_)" | Add-Content $LogFileError
            }
        }
    }

 

Bon courage au service IT pour le nombre d'appel qu'il va recevoir. 

 

O365 : Réaliser un hard match

 

Comme expliqué dans l'article précédent "O365 : Soft Match (SMTP) et Hard Match (ImmutableID)" le Hard Match intervient lorsque le Soft Match n'a pas fonctionné.

Les étapes à suivre sont les suivantes:

  1. Récupérer le GUID du compte dans l'Active Direcotry
  2. Convertir ce dernier en ImmutableID
  3. Appliqué cet ImmutableID au compte Azure Active Directory
  4. Relancer une synchronisation via Ad connect 

Voici donc les commandes pour cela.

# Get GUID for User
$User = Get-ADUser jdupont | select ObjectGUID,UserPrincipalName
$Upn = $User.UserPrincipalName
$Guid = $User.ObjectGUID.Guid

# Convert GUID to ImmutableID
$ImmutableId = [System.Convert]::ToBase64String(([GUID]($User.ObjectGUID)).tobytearray())

# Connect MsolService
Connect-Msolservice

# Set ImmutableID to msoluser
Set-MsolUser -UserPrincipalName $Upn -ImmutableId $ImmutableId

 

Et voila, vous n'avez plus qu'a relancer une Synchor AD Connect via la commande suivante.

Start-ADSyncSyncCycle -PolicyType Delta

 

 

Powershell : List all External users which have access to your Teams

"Peut-on savoir si des externes ont accès aux sites Teams ? Et si oui lesquels ?" est une question qui revient souvent quand on parle droits d'accès et O365.

Pour le savoir rien de plus simple, Powershell et de la patience.

 

#Connect-MicrosoftTeams
Connect-MicrosoftTeams

# Define Variables
$ArrayTeamsMembers = @()
$AllTeams = Get-Team

$AllTeams | foreach {
    $GroupId = $_.GroupId
    $DisplayName = $_.DisplayName
    $Visibility = $_.Visibility
    $Archived = $_.Archived
    $MailNickName = $_.MailNickName
    $Description = $_.Description

    # Search External members
    $Members = Get-TeamUser -GroupId $GroupId 
    $External = $Members.where({$_.Role -eq "Guest"})

    If ($External.count -ne 0) {
        Write-Host "External User(s) are present in $DisplayName" -ForegroundColor Green
        $External.count
        $External | foreach {
            $UserId = $_.UserId
            $User = $_.User

            $ArrayTeamsMembers += New-Object psobject -Property @{
                GroupId = $GroupId
                DisplayName = $DisplayName
                Visibility = $Visibility
                Archived = $Archived
                MailNickName = $MailNickName
                Description = $Description
                UserId = $UserId
                User = $User
                }
            $UserId = $null
            $User = $null
            }
        }

    $GroupId = $null
    $DisplayName = $null
    $Visibility = $null
    $Archived = $null
    $MailNickName = $null
    $Description = $null
    $Members = $null
    $External = $null
    }

$ArrayTeamsMembers | Export-Csv c:\temp\ExternalTeamsMembers.csv -Encoding UTF8 -Delimiter ";" -NoTypeInformation

 

 

Powershell : Extract Teams phone numbers

Avez vous déjà essayé d'exporter les numéros de Téléphone Teams depuis le portail d'administration ? Je vous en prie faites l'essai... Frustrant non ?

Voici comment exporter les numéros Teams dans un fichier CSV via Powershell

# Variables Definition
$Array = @()
$Csv = "C:\temp\TeamsNumbers.csv"

# Skype Online Connection
Import-Module SkypeOnlineConnector
$sessionCS = New-CsOnlineSession -OverrideAdminDomain assystem.com
Import-PSSession $sessionCS

# Azure AD Connection
Connect-AzureAD

# Collect All Voice User
$AllVoiceUsers = Get-CsOnlineVoiceUser

# Collect Data and store them in array
$AllVoiceUsers | foreach {
    $Name = $_.Name
    $Id = $_.Id
    $SipDomain = $_.SipDomain
    $Number = $_.Number
    $LicenseState = $_.LicenseState
    $UsageLocation = $_.UsageLocation
    $EnterpriseVoiceEnabled = $_.EnterpriseVoiceEnabled

    $CurrentUser = Get-AzureADUser -ObjectId $Id
    $Mail = $CurrentUser.Mail
    $Upn = $CurrentUser.UserPrincipalName

    $Array += New-Object psobject -Property @{
        Name = $Name
        Id = $Id
        SipDomain = $SipDomain
        Number = $Number
        LicenseState = $LicenseState
        UsageLocation = $UsageLocation
        EnterpriseVoiceEnabled = $EnterpriseVoiceEnabled
        Mail = $Mail
        Upn = $Upn     
        }
    $Name = $null
    $Id = $null
    $SipDomain = $null
    $Number = $null
    $LicenseState = $null
    $UsageLocation = $null
    $EnterpriseVoiceEnabled = $null
$CurrentUser = $null
    $Mail = $null
    $Upn = $null
    }

# Export Data
$Array | Export-Csv $Csv -Delimiter ";" -Encoding UTF8 -NoTypeInformation

 

 

Script – Attribution de droits utilisateur dans la Local Policy

Le script ci-dessous prend en paramètre un compte et un nom de Privilège pour l’attribution des ‘User Rights Assignment’ au niveau de la 'Local Policy' d’une machine.

LocPolicyAddUserRight.ps1 (5,75 kb)

 

#### SCRIPT: ADD NEW ACCOUNT IN USER RIGHT ASSIGNMENT OF LOCAL SECURITY POLICY

# SCRIPTNAME : LocPolicyAddUserRight.ps1

# PARAMETERS:
# User: Account to grant
# PrivilegeName : See $AccesList to select right name
# ConfFilesPath : Path to export/import Conf File

# EXAMPLE: LocPolicyAddUserRight.ps1 -User MyDomain\MyAccount -PrivilegeName SeTimeZonePrivilege -ConfFilesPath C:\



Param
(
$User,
$PrivilegeName,
$ConfFilesPath
)



#USER RIGHT ASSIGNMENT LIST

[array]$AccessList = $(
"Access Credential Manager as a trusted caller = SeTrustedCredManAccessPrivilege"
"Access this computer from the network = SeNetworkLogonRight"
"Act as part of the operating system;SeTcbPrivilege"
"Add workstations to domain = SeMachineAccountPrivilege"
"Adjust memory quotas for a process = SeIncreaseQuotaPrivilege"
"Allow log on locally = SeInteractiveLogonRight"
"Allow log on through Remote Desktop Services = SeRemoteInteractiveLogonRight"
"Back up files and directories = SeBackupPrivilege"
"Bypass traverse checking = SeChangeNotifyPrivilege"
"Change the system time = SeSystemtimePrivilege"
"Change the time zone = SeTimeZonePrivilege"
"Create a pagefile = SeCreatePagefilePrivilege"
"Create a token object = SeCreateTokenPrivilege"
"Create global objects = SeCreateGlobalPrivilege"
"Create permanent shared objects = SeCreatePermanentPrivilege"
"Create symbolic links = SeCreateSymbolicLinkPrivilege"
"Debug programs = SeDebugPrivilege"
"Deny access to this computer from the network = SeDenyNetworkLogonRight"
"Deny log on as a batch job = SeDenyBatchLogonRight"
"Deny log on as a service = SeDenyServiceLogonRight"
"Deny log on locally = SeDenyInteractiveLogonRight"
"Deny log on through Remote Desktop Services = SeDenyRemoteInteractiveLogonRight"
"Enable computer and user accounts to be trusted for delegation = SeEnableDelegationPrivilege"
"Force shutdown from a remote system = SeRemoteShutdownPrivilege"
"Generate security audits = SeAuditPrivilege"
"Impersonate a client after authentication = SeImpersonatePrivilege"
"Increase a process working set = SeIncreaseWorkingSetPrivilege"
"Increase scheduling priority = SeIncreaseBasePriorityPrivilege"
"Load and unload device drivers = SeLoadDriverPrivilege"
"Lock pages in memory = SeLockMemoryPrivilege"
"Log on as a batch job = SeBatchLogonRight"
"Log on as a service = SeServiceLogonRight"
"Manage auditing and security log = SeSecurityPrivilege"
"Modify an object label = SeRelabelPrivilege"
"Modify firmware environment values = SeSystemEnvironmentPrivilege"
"Perform volume maintenance tasks = SeManageVolumePrivilege"
"Profile single process = SeProfileSingleProcessPrivilege"
"Profile system performance = SeSystemProfilePrivilege"
"Remove computer from docking station = SeUndockPrivilege"
"Replace a process level token = SeAssignPrimaryTokenPrivilege"
"Restore files and directories = SeRestorePrivilege"
"Shut down the system = SeShutdownPrivilege"
"Synchronize directory service data = SeSyncAgentPrivilege"
"Take ownership of files or other objects = SeTakeOwnershipPrivilege"

)



Function GetUserSID($UserName)
{
(New-Object System.Security.Principal.NTAccount($UserName)).Translate([System.Security.Principal.SecurityIdentifier]).value
}




Function Parse-SecPol($CfgFile){ 
    secedit /export /cfg "$CfgFile" | out-null
    $obj = New-Object psobject
    $index = 0
    $contents = Get-Content $CfgFile -raw
    [regex]::Matches($contents,"(?<=\[)(.*)(?=\])") | %{
        $title = $_
        [regex]::Matches($contents,"(?<=\]).*?((?=\[)|(\Z))", [System.Text.RegularExpressions.RegexOptions]::Singleline)[$index] | %{
            $section = new-object psobject
            $_.value -split "\r\n" | ?{$_.length -gt 0} | %{
                $value = [regex]::Match($_,"(?<=\=).*").value
                $name = [regex]::Match($_,".*(?=\=)").value
                $section | add-member -MemberType NoteProperty -Name $name.tostring().trim() -Value $value.tostring().trim() -ErrorAction SilentlyContinue | out-null
            }
            $obj | Add-Member -MemberType NoteProperty -Name $title -Value $section
        }
        $index += 1
    }
    return $obj
}




Function Set-SecPol($Object, $CfgFile){
   $OldSecPool.psobject.Properties.GetEnumerator() | %{
        "[$($_.Name)]"
        $_.Value | %{
            $_.psobject.Properties.GetEnumerator() | %{
                "$($_.Name)=$($_.Value)"
            }
        }
    } | out-file $CfgFile -ErrorAction Stop
    secedit /configure /db c:\windows\security\local.sdb /cfg "$CfgFile" #| out-null
}



# Export Original Conf
$OldSecPool = Parse-SecPol -CfgFile "$ConfFilesPath`Old.inf"

# Get user SID
try
    {
    $newsid = GetUserSID -UserName $User
    }
catch
    {
    $Message = "'$User' user SID has not been found"
    Write-Host -F Red $Message.ToUpper()
    exit 1 
    }


# 
if ($OldSecPool.'Privilege Rights'.$PrivilegeName -eq $null)
    {
    $Message = "'$PrivilegeName' privilege has not been found. Check Name. `n "
    Write-Host -F Red $Message.ToUpper()
    Write-Host "--- ACCESS RIGHTS NAMES ---:`n"
    $AccessList
    exit 1
    }
Else
    {
    # get original value
    $OldValue = $OldSecPool.'Privilege Rights'.$PrivilegeName
    # Add new SID to the string with required additional characters
    $NewValue = $OldValue+",*$newsid"
    # Set New Value
    $OldSecPool.'Privilege Rights'.$PrivilegeName = $NewValue
    }


$result = Set-SecPol -Object $OldValue -CfgFile "$ConfFilesPath`New.inf" 

if ($result[-2] -like "*successfully*")
    {
    $Message = "OK - '$User' has been granted '$PrivilegeName' `n $($result[-2]) `n $($result[-1])"
    write-host -F Green $Message.ToUpper()
    exit 0
    }

Else
    {
    $Message = "Error during grant of rights"
    write-host -F Red $Message.ToUpper()
    $result
    exit 1
    }

 

 

 

Active Directory : Restaurer des objets de la corbeille sous 2008

Quand on a l'habitude d'utiliser la console Active Directory Administrative Center pour restaurer des objets AD et que l'on désire restaurer des objets AD sur un Active Directory sous Windows server 2008, on peut se sentir décontenancer.

Eh oui pas de console ADAC sur Windows Server 2008, bon alors on fait quoi ? Powershell !!!

Pour lister tous les objets de la corbeille :

# List All Deleted objects
$AllDeletedObjects = Get-ADObject -ldapFilter:"(msDS-LastKnownRDN=*)" –IncludeDeletedObjects -Properties *

Ensuite libre à vous de filtrer sur les types d'objets, nom et autres propriétés.

# List All Deleted Groups
$DeletedGroups = $AllDeletedObjects | Where {$_.ObjectClass -eq "group"}

Et enfin restaurer ce qui vous intéresse dans l'OU de destination qui convient (ici le groupe "GS-Office-E3" restaurer dans l'OU "RestaurationPlace")

$ToRestore = $DeletedGroups | Where {$_.Name -like "GS-Office-E3"}
$DeletedGroups | Restore-ADObject -TargetPath "OU=RestorationPlace,DC=Demo,DC=corp"

 

Powershell : Générateur de mot de passe

Vous en avez marre de chercher un nouveau mot de passe, ou vous devez générer des mots de passe pour u grand nombre d'utilisateur ?

Voici quelques lignes Powershell que vous pouvez utiliser dans une fonction ou  dans une boucle pour générer un mot de passe.

$TinyCharacters = "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"
$CapitalCharacters = "A",",B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"
$NumeralCharacters = "0","1","2","3","4","5","6","7","8","9"
$SpecialCharacters = "!","$","%","&","/","(",")","=","?","}","]","[","{","@","#","*","+"
$Password = $null

1..3 | ForEach-Object {
    $Password += Get-Random -Minimum 1 $TinyCharacters
    $Password += Get-Random -Minimum 1 $CapitalCharacters
    $Password += Get-Random -Minimum 1 $NumeralCharacters
    $Password += Get-Random -Minimum 1 $SpecialCharacters
    }
$Password

Ces quelques ligne vous permettront de générer un mot de passe de 12 caractères, pour un mot de passe plus long changez la valeur de 1..3 à 1..4 ou 1..5

Script - Modification du fichier host et test de la resolution a distance

Le script ci-dessous propose de remplacer a distance le contenu du fichier de resolution c:\windows\system32\etc\hosts et d'effectuer un test de resolution des nouveaux noms.

Remote_ModifyHostFiles_And_Test.ps1 (3,61 kb)

# SCRIPT TO MODIFY HOSTS FILE ON TARGET SERVER AND REMOTELY TEST RESOLUTION

Param(
$SrvList = ("Srv1","Srv2"),
$HostFilePath = "C:\Windows\System32\drivers\etc\hosts",
$cred = $(Get-Credential -Credential MyDomain\Me),
$Host1 = "Host1",
$IP1 = "0.0.0.1",
$Host2 = "Host2",
$IP2 = "0.0.0.2"
)


$cred = $(Get-Credential -Credential MyDomain\Me)




# host file
$hostcontent = 
"# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
#      102.54.94.97     rhino.acme.com          # source server
#       38.25.63.10     x.acme.com              # x client host

# localhost name resolution is handled within DNS itself.
#	127.0.0.1       localhost
#	::1             localhost


# MY SERVERS
$Host1     $IP1
$Host2     $IP2
"


# FUNCTIONS
function Spec-Ping
{
Param($HostToPing)
Try {
            Test-Connection -ComputerName $HostToPing -Count 1 -ErrorAction Stop
            } 
            Catch [System.Net.NetworkInformation.PingException]
            {
            $pingresult = "PING ERROR TO $HostToPing - CHECK NAME RESOLVING"
            }

}


# OPENING WINRM SESSIONS ON MGS SERVERS
$SrvList | foreach {


            try
            {
            New-PSSession -Name $($_.Substring(0,13)) -ComputerName $_ -Credential $cred
            }
            catch
            {
            write-host -F red "Erreur lors de la creation de la session Winrm vers $_"
            }

}





$sessions = Get-PSSession

Foreach ($sess in $sessions)
    {
        
        Invoke-Command -Session $sess -ScriptBlock {Param($hostcontent,$HostFilePath) Set-Content -Path $HostFilePath -value $hostcontent} -ArgumentList $hostcontent,$HostFilePath
    
}



Write-Host "`n--- PING RESULT:"

Foreach ($sess in $sessions) 
    {
        
        # First Host Test
        $pingresult = Invoke-Command -Session $sess -ScriptBlock ${Function:Spec-Ping} -ArgumentList $Host1
            
            
            
             switch -Wildcard ($pingresult.value)
             {
             "PING ERROR*" {write-host -ForegroundColor red $pingresult.value}
             }
             

             Switch ($pingresult.IPV4Address.IPAddressToString)
                {
                $IP1 {Write-Host "OK -- $Host1 is resolved by "$sess.ComputerName" to $IP1"}
                default {Write-Host "KO - Unable to get resolution of $Host1 on"$sess.ComputerName""}
                }
    



        
         # Second Host Test
         $pingresult = Invoke-Command -Session $sess -ScriptBlock ${Function:Spec-Ping} -ArgumentList $Host2
         switch -Wildcard ($pingresult.value)
             {
             "PING ERROR*" {write-host -ForegroundColor red $pingresult.value}
             }
             

             Switch ($pingresult.IPV4Address.IPAddressToString)
                {
                $IP2 {Write-Host "OK -- $Host2 is resolved by "$sess.ComputerName" to $IP2"}
                default {Write-Host "KO - Unable to get resolution of $Host2 on"$sess.ComputerName""}
                }

    }     




#Remove Pssessions
Remove-PSSession *

 

 

Teams : Exporter les utilisateurs et leurs numéros

Vous souhaitez exporter la liste de vos numéros Teams et les utilisateurs associés, vous devrez utiliser les commandes SkypeOnline (et oui pas Teams...) suivantes :

# Define variable
$Domain = Read-Host -Prompt "Quel est votre domain ?"
$Csv = "C:\temp\TeamsPhoneNumbers.csv"

# Skype Online Connection
Import-Module SkypeOnlineConnector
$sessionCS = New-CsOnlineSession -OverrideAdminDomain $Domain
Import-PSSession $sessionCS

# Collect All Voice User
$AllVoiceUsers = Get-CsOnlineVoiceUser

# Export to Csv
$AllVoiceUsers | Export-Csv $Csv -Delimiter ";" -Encoding UTF8 -NoTypeInformation

 

SCOM - Script - Exemple d'inventaire avec les classes "Principales".

L'idée du script ci-dessous est de proposer une forme d'inventaire des applications couvertes par une infra SCOM, en utilisant une liste, a maintenir, des principales classes d'objets tel que  'Active Directory Domain Controller Computer Role'  ou encore 'IIS Server Role'.

Il est donc necessaire de maintenir un minimum dans le temps, le contenu de $ClassList, et la correspondance faites entre les pattern de nom de classes et les application correspondantes.

SCOM_Inventory_with_Main_Classes.ps1 (12,71 kb)

 

### SCOM INVENTORY USING APPLICATION MAIN CLASS ###


#Parameters
Param(
$MS= "MyMS.MyDomain",
$cred = $(Get-Credential "MyDomain\")
)


# List of all 'Top' Classes for which we need to get instances
$ClassList=(
# ACTIVE DIRECTORY
'Active Directory Domain Controller Computer Role',` # ACTIVE DIRECTORY
'Certificate Service',  # ACTIVE DIRECTORY CERTIFICATE SERVICES
'Federation Server',    # ACTIVE DIRECTORY FEDERATION SERVICES 

# CITRIX
'Managed Citrix Presentation Server',  # CITRIX


# EXCHANGE
'Microsoft Exchange 2010 Server', # EXCHANGE
'Exchange 2007 Server Role', # EXCHANGE
'Exchange 2013 Server', # EXCHANGE

# LYNC/SKYPE
'LS Server Role', # LYNC/SKYPE

# SHAREPOINT
'SharePoint Server', # SHAREPOINT

# SQL
'SQL Server 2008 DB Engine', # SQL (SQL 2008)
'SQL Server 2012 DB Engine', # SQL (SQL 2012)
'SQL Server 2014 DB Engine', # SQL (SQL 2014)
'SQL Server 2016 DB Engine', # SQL (SQL 2016)
'MSSQL on Windows: DB Engine', # SQL (SQL 2017+)

# SQL SSIS
'SQL Server 2014 Integration Services', # SQL SSIS (SQL 2014)
'SQL Server 2016 Integration Services', # SQL SSIS (SQL 2016)
'MSSQL on Windows Integration Services: Local Instance', # SQL SSIS (SQL 2017+)

# SQL SSAS
'SSAS 2008 Instance', # SQL SSAS (SQL 2008)
'SSAS 2012 Instance', # SQL SSAS (SQL 2012)
'SSAS 2014 Instance', # SQL SSAS (SQL 2014)
'SSAS 2016 Instance', # SQL SSAS (SQL 2016)
'MSSQL Analysis Services: Generic Instance', # SQL SSAS (SQL 2017+)

# SQL SSRS
'Microsoft SQL Server 2008 Reporting Services (Native Mode)', # SQL SSRS (SQL 2008)
'Microsoft SQL Server 2012 Reporting Services (Native Mode)', # SQL SSRS (SQL 2012)
'Microsoft SQL Server 2014 Reporting Services (Native Mode)', # SQL SSRS (SQL 2014)
'Microsoft SQL Server 2016 Reporting Services (Native Mode)', # SQL SSRS (SQL 2016)
'MSSQL Reporting Services: Instance (Native Mode)' , # SQL SSRS (SQL 2017)

# WINDOWS CLUSTER NODES
'Cluster Node', # WINDOWS CLUSTER

# IIS
'IIS Server Role', # IIS

# WINDOWS PRINT SERVER
'Print Services Role', # WINDOWS PRINT SERVER

# WINDOWS DNS
'Windows DNS Server', # WINDOWS DNS

# WSUS
'WSUS 3.0 Server', # WSUS
'Microsoft Windows Server Update Services 2012 R2', # WSUS
'Microsoft Windows Server Update Services 2016' # WSUS


  
)


#Import of SCOM module
try
{
Import-Module -Name OperationsManager -ErrorAction stop
}
catch
{
write-host -ForegroundColor red "Error during import of SCOM Module"
}

#Connection to $MS management server
New-SCOMManagementGroupConnection -ComputerName $MS -Credential $cred



# Create an empty tableau
$Finaltableau = @()


# Get All Instances for each class
foreach ($classname in $ClassList)
{

    
    $class = Get-SCOMClass -DisplayName $classname

    # If class is found we can go on  
    If($class)
    {
    


                $instances += Get-SCOMClassInstance -Class $class -ErrorAction SilentlyContinue



                foreach ($inst in $instances)
                {

                $obj = New-Object psobject


                    switch -Regex ($classname)
                    {
                    # ACTIVE DIRECTORY
                    "Active Directory Domain Controller Computer Role"  
                                            {

                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.path
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "ACTIVE DIRECTORY"
                                            }
    
    
                    # ACTIVE DIRECTORY CERTIFICATE SERVICES
                    "Certificate Service"   
                                            {
                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.displayname
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "ACTIVE DIRECTORY CERTIFICATE SERVICES"
                                            }


                    # ACTIVE DIRECTORY FEDERATION SERVICES
                    "Federation Server"   
                                            {
                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.displayname
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "ACTIVE DIRECTORY FEDERATION SERVICES"
                                            }


                    # CITRIX
                    ".*Citrix.*"   
                                            {
                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.displayname
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "CITRIX"
                                            }

                    
                    
                    # EXCHANGE
                    ".*Exchange.*"   
                                            {
                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.displayname
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "EXCHANGE"
                                            }          
                    
                    
                    # LYNC/SKYPE
                    ".*LS Server.*"   
                                            {
                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.Path
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "LYNC/SKYPE"
                                            }          



                    # SHAREPOINT
                    ".*sharepoint.*"   
                                            {
                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.Path
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "SHAREPOINT"
                                            }          




                    # SQL DB ENGINE
                    "SQL Server.*DB Engine"   
                                            {
                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.Path
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "SQL"
                                            }          



                    
                    "MSSQL on Windows: DB Engine"
   
                                            {
                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.'[Microsoft.SQLServer.Windows.DBEngine].PrincipalName'.value
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "SQL"
                                            }          





                    # SQL BI
                    ".*(SQL Server.*Integration Services|Analysis Services|Reporting Services|SSAS).*"
                       
                                            {
                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.Path
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "SQL BI"
                                            }          


                    
                    "MSSQL on Windows Integration Services: Local Instance"
                                            
                                            {
                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.'[Microsoft.SQLServer.IS.Windows.LocalInstance].PrincipalName'.value
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "SQL BI"
                                            }      






                    # WINDOWS CLUSTER
                    ".*Cluster Node.*"   
                                            {
                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.DisplayName
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "WINDOWS CLUSTER"
                                            }          




                    # IIS
                    ".*IIS Server Role.*"   
                                            {
                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.Path
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "IIS"
                                            }          




                    # WINDOWS PRINT SERVER
                    ".*Print Services Role.*"   
                                            {
                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.Path
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "WINDOWS PRINT SERVER"
                                            }          




                    # WINDOWS DNS
                    ".*Windows DNS Server.*"   
                                            {
                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.Path
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "WINDOWS DNS"
                                            }          




                    # WSUS
                    ".*(Windows Server Update|WSUS).*"   
                                            {
                                            $obj | Add-Member -Name "COMPUTER" -membertype Noteproperty -Value $inst.Path
                                            $obj | Add-Member -Name "CLASS" -membertype Noteproperty -Value $classname
                                            $obj | Add-Member -Name "MAIN ROLE" -membertype Noteproperty -Value "WSUS"
                                            }          



                    }



                $Finaltableau += $obj

                }

    }


    Else
    {
    $ClassError = "'$classname' class was not found`n"
     
    }



# Clear of $instances variable for each $classname loop
Clear-Variable instances

}



# Display Final Result
$Finaltableau



# Display If some class was not found
If ($ClassError)
{
Write-Host -F Red "SOME CLASSES WAS NOT FOUND:`n$ClassError"
}