PI Services

Le blog des collaborateurs de PI Services

Powershell : Trouver la date de dernière connexion d'un utilisateur.

Comme vous le savez l'un des attributs qui nous est le plus important pour déterminer l'inactivité d'un compte est l'attribut "LastLogon", ce dernier permet de connaitre la dernière authentification valide d'un compte Active Directory.

Le problème c'est que ce dernier n'est pas répliqué entre les DCs, attention je parle bien du "LastLogon" et non le "LastLogonTimeStamp" qui lui l'est mais, ne détient pas la valeur réelle de la dernière authentification réussie.

Le "LastLogon" n'est pas répliqué entre les DCs pour une raison simple, éviter les "tempêtes" de réplication entre les DCs, imaginez, à chaque ouverture / déverrouillage de session cette valeur change, si vous pensez au nombre de fois que sont déverrouillées des sessions à la journée, cela vous donne une idée des réplications qui seraient nécessaires, par conséquent cet attribut n'est pas répliqué.

Ce qui pose donc problème c'est qu'il n'est pas possible d'avoir la réelle valeur de "LastLogon" sans devoir interroger l'intégralité des DCs et faire une comparaison de cette dernière.

Voici un script qui fera les requêtes et comparaisons pour vous, toutefois gardez à l'esprit qu'il va pour chaque utilisateur demander la valeur de l'ttribut "LastLogonà chaque DC, par conséquent ce script peut prendre plus ou moins de temps à s'exécuter en fonction du nombre d'utilisateur et du nombre de DCs.

$Array = @()
$AllDC = Get-ADDomainController -Filter * | sort Name | select Name
$AllEnabledUsers = Get-ADUser -Filter {Enabled -eq $true} | select Samaccountname

$AllEnabledUsers | select -First 5 | foreach {
    $CurrentUser = $_.Samaccountname
    $RealLastLogon = $null

    $AllDC | foreach {
        $CurrentDC = $_.Name

        Try {
            $Logon = Get-ADUser $CurrentUser -Properties LastLogon -Server $CurrentDC
            $LastLogon = [Datetime]::FromFileTime($Logon.LastLogon)
            If ($LastLogon -gt $RealLastLogon) {
                $RealLastLogon = $LastLogon
                }

            # Release variable
            $LastLogon = $null
            }
        Catch {
            Write-Warning $($_)
            }

        # Release variable
        $CurrentDC = $null
        }
    
    # Store Data
    $Array += New-Object psobject -Property @{
        DistinguishedName = $Logon.DistinguishedName
        Enabled = $Logon.Enabled
        GivenName = $Logon.GivenName
        Name = $Logon.Name
        ObjectClass = $Logon.ObjectClass
        ObjectGUID = $Logon.ObjectGUID
        SamAccountName = $Logon.SamAccountName
        SID = $Logon.SID
        Surname = $Logon.Surname
        UserPrincipalName = $Logon.UserPrincipalName
        LastLogon = $RealLastLogon
        }
        
    # Release Variables
    $CurrentUser = $null
    $RealLastLogon = $null
    }

Il ne vous restera qu'a examiner la sortie en rappelant la variable "$Array" ou à l'exporter dans un CSV comme ci-dessous par exemple.

$Array | Export-Csv C:\Temp\AllUsersWithLastLogon.csv -Delimiter ";" -Encoding UTF8 -NoTypeInformation

 

Si la valeur "LastLogon" est égale à "01/01/1601 01:00:00" cela veut dire que le compte ne s'est jamais connecté

Script - API Vmware VCenter - Get VM infos

Le script ci-dessous est un exemple simple de l'utilisation de l'API Rest de Vcenter pour recuperer toute sorte d'information, nottament sur les machines virtuelles.

A noter que la limitation du nombre d'objet retournés oblige simplement a recuperer dans un premier temps la liste des hosts, puis par host la liste des VMs.

 

### QUERY VCENTER REST API TO GET VM LIST ###


$user = ‘myaccount’
$pswd = Read-Host -Prompt "Enter Password"
$vCenterName = ‘MyVcenter’
$encoded = [System.Text.Encoding]::UTF8.GetBytes(($user, $pswd -Join ‘:’))
$encodedPassword = [System.Convert]::ToBase64String($Encoded)
$authHeader = @{
Authorization = "Basic $($EncodedPassword)"
}
$sRest = @{
Method = ‘Post’
Uri = "https://$($vCenterName)/rest/com/vmware/cis/session"
Headers = $authHeader
}
$result = Invoke-RestMethod @sRest


# Get TokenID
$authHeader = @{
‘vmware-api-session-id’ = $result.value
}


# Get All Hosts
$gethosts = "https://$($vCenterName)/rest/vcenter/host"
$resultgethost = Invoke-RestMethod -Uri $gethosts -Headers $authHeader
$hostidlist = $resultgethost.value.host


# For each host, get all VMs

foreach ($hostid in $hostidlist)
{
$get_vm = "https://$($vCenterName)/rest/vcenter/vm?filter.hosts=$hostid"
$resultvm = Invoke-RestMethod -Uri $get_vm -Headers $authHeader
[array]$FinalTableau += $resultvm
}

# Display Result
$FinalTableau.value | select name,power_state


# Output to CSV
$CsvTab = $FinalTableau.value | select name,power_state | ConvertTo-Csv -Delimiter ';' -NoTypeInformation
$CsvTab | Out-File .\VMList.csv

 

 

 

 

Script - Invoquer un update d'un agent Trend OSC a distance

Le script ci-dessous propose d'utiliser a distance la commande "PccNTMon.exe -u" sur un agent Trend OSC pour invoquer une mise a jour, en direction de son serveur OSC de rattachement.

 

#### RemoteInvokeTrendOSCAgentUpdate.ps1: REMOTELY INITIATE AN UPDATE OF A TREND OSC AGENT ####

## COMMENT: The script execute command through PSSession and open a RDP window to allow interactive use of "OSCAgentExe -u" ###

param(
# Target Computer
$Computer = "MyTarget",
# Cred
$Cred = $(Get-Credential -Credential "MyAccount"),
# OSC Agent Folder
$OSCAgentFolder = "C:\Program Files (x86)\Trend Micro\OfficeScan Client\",
# OSC Client Process
$OSCClientProc = "PccNTMon"
)


# OSC Agent Exe
$OSCAgentExe = "$OSCAgentFolder$OSCClientProc`.exe"


# FUNCTIONS
function Set-WindowState {
	
	[CmdletBinding(DefaultParameterSetName = 'InputObject')]
	param(
		[Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
		[Object[]] $InputObject,

		[Parameter(Position = 1)]
		[ValidateSet('FORCEMINIMIZE', 'HIDE', 'MAXIMIZE', 'MINIMIZE', 'RESTORE',
					 'SHOW', 'SHOWDEFAULT', 'SHOWMAXIMIZED', 'SHOWMINIMIZED',
					 'SHOWMINNOACTIVE', 'SHOWNA', 'SHOWNOACTIVATE', 'SHOWNORMAL')]
		[string] $State = 'SHOW'
	)

	Begin {
		$WindowStates = @{
			'FORCEMINIMIZE'		= 11
			'HIDE'				= 0
			'MAXIMIZE'			= 3
			'MINIMIZE'			= 6
			'RESTORE'			= 9
			'SHOW'				= 5
			'SHOWDEFAULT'		= 10
			'SHOWMAXIMIZED'		= 3
			'SHOWMINIMIZED'		= 2
			'SHOWMINNOACTIVE'	= 7
			'SHOWNA'			= 8
			'SHOWNOACTIVATE'	= 4
			'SHOWNORMAL'		= 1
		}

		$Win32ShowWindowAsync = Add-Type -MemberDefinition @'
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
'@ -Name "Win32ShowWindowAsync" -Namespace Win32Functions -PassThru

		if (!$global:MainWindowHandles) {
			$global:MainWindowHandles = @{ }
		}
	}

	Process {
		foreach ($process in $InputObject) {
			if ($process.MainWindowHandle -eq 0) {
				if ($global:MainWindowHandles.ContainsKey($process.Id)) {
					$handle = $global:MainWindowHandles[$process.Id]
				} else {
					Write-Error "Main Window handle is '0'"
					continue
				}
			} else {
				$global:handle = $process.MainWindowHandle
			}

			$Win32ShowWindowAsync::ShowWindowAsync($handle, $WindowStates[$State]) | Out-Null
			Write-Verbose ("Set Window State '{1} on '{0}'" -f $handle, $State)
		}
	}
}




# FOR EACH COMPUTER
foreach ($Comp in $Computer) {


# Create PSSession
try
{
New-PSSession -ComputerName $Comp -Credential $Cred -Name "PSSession_$Comp" -ErrorAction Stop
}
catch
{
write-host -F Red "Error during New-PSSession `n $($Error[0].Exception)"
exit 1 
}


# Get PSsession
$RemoteSession = Get-PSSession -Name "PSSession_$Comp"


# Open RDP Session (needed because the invoke update is interactive)
cmdkey /generic:TERMSRV/$Comp /user:($Cred.UserName) /pass:($Cred.GetNetworkCredential().Password)

mstsc /v:$Comp /w:50 /h:50

Write-Host "Wait for 10 sec..."
Start-Sleep -Seconds 10


# Get the RDP process Id to kill it at the end
$ProcId = (Get-Process mstsc | Where-Object {$_.MainWindowTitle -like "*$Comp*"}).Id


# Minimize RDP Window
(Get-Process mstsc | Where-Object {$_.MainWindowTitle -like "*$Comp*"}) | foreach {Set-WindowState $_ HIDE}


Write-Host "Wait for 10 sec..."
Start-Sleep -Seconds 10



# Invoke Update
write-host "Invoking update..."
Invoke-Command -Session $RemoteSession -ScriptBlock {Start-Process -FilePath $Using:OSCAgentExe -ArgumentList "-u"} 


        # Wait while update (see for update of ofcscan.ini file)
        do {
            Write-Host "Wait while update..."
           }

        until

           (
            (Invoke-Command -Session $RemoteSession -ScriptBlock {Get-ChildItem -Path "$Using:OSCAgentFolder`ofcscan.ini" | Where-Object {$_.LastWriteTime -gt $(get-date).AddMinutes(-15)}})
           )


Write-Host "Wait for 10 sec..."
Start-Sleep -Seconds 10



# Close RDP Session
write-host "Logoff RDP Session on $Comp..."
Invoke-Command -Session $RemoteSession -ScriptBlock {`
    $CredUser = $($Using:Cred.UserName)
    $sessions = quser | Where-Object {$_ -match $CredUser} ; 
    $sessionIds = ($sessions -split ' +')[2] ; 
    Write-Host "Found $(@($sessionIds).Count) user login(s) on computer.";
    $sessionIds | ForEach-Object {Write-Host "Logging off session id [$($_)]..." ; logoff $_}
     
     }


# Close RDP Session Disconnect Window by killing process
Get-Process -Id $ProcId | stop-process


}

# Remove Pssession
Get-PSSession | Remove-PSSession

 

Powershell : Supprimer les KB "Declined"

Quand on veut faire du ménage sur son WSUS et également dans la base de ce dernier, il peut s'avérer utile de supprimer les KB dont le statut est "IsDeclied".

Pour réaliser cela rien de plus simple que quelques lignes de Powershell et de la patience (beaucoup de patience si ça fait longtemps que cela n'a pas été fait).

Connexion

# Define Server Name
$WsusServer = "Mon-Server-WSUS"
# Define connection port
$WsusPort = "8530"

# Initiate Connection
[void][reflection.assembly]::loadwithpartialname("microsoft.updateservices.administration")
$Wsus = [microsoft.updateservices.administration.adminproxy]::getupdateserver($WsusServer,$false,$WsusPort)

# Verifying connection (Should be return the name of the wsus server)
$Wsus.name

 

Définition des Updates à supprimer

# List All Kb in WSUS database
$AllKb = $Wsus.GetUpdates()

# Filter on "IsDeclined" status
$IsDeclined = $AllKb.Where({$_.IsDeclined -eq $True})

 

Suppression des KB

# Delete KB
$IsDeclined | foreach {
    # Define Current Id
    $Id = $_.Id
    Try {
        $Wsus.DeleteUpdate($Id.UpdateId.ToString())
        }
    Catch {
        Write-Warning $($_)
        }
    # Release variable
    $Id = $null
    }

Cette commande est un peu longue mais si elle est exécutée régulièrement, la purge dure moins longtemps.

Powershell : Change the owner of my AD objects

Comme évoqué dans l'article  "Powershell : Who's the owner of my AD objects", il est possible que votre AD contienne des objets dont le propriétaire ne soit aucun des suivants :

  • "Enterprise Admins"
  • "Domain Admins"
  • "Administrators"

Afin de remettre "Domain Admins" comme propriétaire des objets, nous utiliserons Powershell pour chacun des types d'objets.

Attention : La variable $NoGood est issue des scripts du précédent article, pensez à vérifier ce qu'elle retourne au préalable.

Unités d'organisation

# Organizational Units
$NoGood | foreach {
    # Current OU
    $DistinguishedName = $_.DistinguishedName

    # Change Owner
    Try {
        # Define Target
        $TargetObject = Get-ADOrganizationalUnit -Identity $DistinguishedName
        $AdsiTarget = [adsi]"LDAP://$($TargetObject.DistinguishedName)"

        # Set new Owner
        $NewOwner = New-Object System.Security.Principal.NTAccount("DOMAINXXX", "Domain Admins")
        $AdsiTarget.PSBase.ObjectSecurity.SetOwner($NewOwner)
        $AdsiTarget.PSBase.CommitChanges()
        }
    Catch {
         Write-Warning $($_)
         $DistinguishedName
        }

    # Release variable
    $DistinguishedName = $null
    }

Les groupes

# Group
$NoGood | foreach {
    # Current Group
    $SamAccountName = $_.SamAccountName

    # Change Owner
    Try {
        # Define Target
        $TargetObject = Get-ADGroup $SamAccountName
        $AdsiTarget = [adsi]"LDAP://$($TargetObject.DistinguishedName)"

        # Set new Owner
        $NewOwner = New-Object System.Security.Principal.NTAccount("DOMAINXXX", "Domain Admins")
        $AdsiTarget.PSBase.ObjectSecurity.SetOwner($NewOwner)
        $AdsiTarget.PSBase.CommitChanges()
        }
    Catch {
        Write-Warning $($_)
        $SamAccountName
        }
    
    # Release variable
    $SamAccountName = $null
    }

Les utilisateurs

# Users
$NoGood | foreach {
    # Current User
    $SamAccountName = $_.SamAccountName

    # Change Owner
    Try {
        # Define Target
        $TargetObject = Get-ADUser $SamAccountName
        $AdsiTarget = [adsi]"LDAP://$($TargetObject.DistinguishedName)"

        # Set new Owner
        $NewOwner = New-Object System.Security.Principal.NTAccount("DOMAINXXX", "Domain Admins")
        $AdsiTarget.PSBase.ObjectSecurity.SetOwner($NewOwner)
        $AdsiTarget.PSBase.CommitChanges()
        }
    Catch {
        Write-Warning $($_)
        $SamAccountName
        }
    
    # Release variable
    $SamAccountName = $null
    }

 

Les ordinateurs

# Computers
$NoGood | foreach {
    # Current  Computer
    $SamAccountName = $_.SamAccountName

    # Change Owner
    Try {
        # Define Target
        $TargetObject = Get-ADComputer $SamAccountName
        $AdsiTarget = [adsi]"LDAP://$($TargetObject.DistinguishedName)"

        # Set new Owner
        $NewOwner = New-Object System.Security.Principal.NTAccount("DOMAINXXX", "Domain Admins")
        $AdsiTarget.PSBase.ObjectSecurity.SetOwner($NewOwner)
        $AdsiTarget.PSBase.CommitChanges()
        }
    Catch {
        Write-Warning $($_)
        $SamAccountName
        }

    # Release variable
    $SamAccountName = $null
    }

 

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 : Who's the owner of my AD objects

Lors de la création d'un objet Active Directory, un propriétaire est inscrit dans l'attribut "Security Descriptor" ce dernier dépend de "qui" a créé le compte.

 

  • Dans un AD sans délégation de droits d'administration.

Si l'utilisateur qui créé l'objet est membre du groupe :

  • "Enterprise Admins" et "Domain Admins" : Le propriétaire sera le groupe "Domain Admins"
  • "Domain Admins" : Le propriétaire sera le groupe "Domain Admins"

  • "Enterprise Admins" : Le propriétaire sera le groupe "Enterprise Admins"

  • Dans un AD avec délégation de droits d'administration.

Lorsque l'on délègue les droits de création d'objets dans Active Directory, le compte utilisé pour créer des objets sera définit comme propriétaire de ces derniers.

Si par exemple je délègue la création d'objets de type "Users" au groupe "SG-Users-Management" et que le membre "Thomas DUPONT" créé un utilisateur, ce dernier sera propriétaire de l'objet utilisateur.

Si demain "Thomas DUPONT" n'est plus membre du groupe (pour n'importe quelle raison), il ne sera plus en mesure de créer / Modifier / Supprimer d'utilisateurs (jusque la tout va bien c'est ce que l'on désire), MAIS (et oui il y en a toujours un) ce ne sera pas vrai pour tous les objets créés par "Thomas DUPONT" précédemment.

Pourquoi ?

Et bien simplement parce que ne l'oublions "Thomas DUPONT" est le propriétaire des objets qu'il a créé, par conséquent il possède par défaut le droit de lecture et modification de l'objet.

Et donc ?

Ne l'oublions pas, le droit "Modify" permet de gérer les autorisations sur un objet, par conséquent il est donc possible pour "Thomas DUPONT" de se donner un droit "Full Control" sur ces objets.

Mais alors comment on identifie ces objets ?

Voici comment vérifier qui est le propriétaire des objets présents dans Active Directory.

  • Dans Active Directory Users and Computers

Comme nous l'avons vu plus haut il est possible via la console Active Directory Users and Computers de voir qui est le propriétaire d'un objet en passant par l'onglet "Security" puis "Advanced", mais bon pour un objet à la limite mais on va pas passer tous les objets à la main, c'est la qu'entre en jeu la seconde solution "Powershell".

  • Avec powershell

 Avec Powershell, il suffit d'utiliser la commande "Get-Aduser" et de récupérer la propriété "nTSecurityDescriptor

$User = Get-ADUser mlemoine -Properties nTSecurityDescriptor

 Ok, mais on en fais quoi de ça me direz vous, c'est pas faux ça nous aide pas trop, alors complétons la commande comme suivant :

$User = Get-ADUser mlemoine -Properties nTSecurityDescriptor
$User.nTSecurityDescriptor.owner

C'est déjà mieux, mais si on vérifiait tout l'AD.

# Create Array
$Array = @()

# Collect AD infos
$Domain = Get-ADDomain | select -ExpandProperty NetBIOSName
$AllUsers = Get-ADUser -Filter * -Properties nTSecurityDescriptor

# Store Info
$AllUsers | foreach {
    $DistinguishedName = $_.DistinguishedName
    $GivenName = $_.GivenName
    $Name = $_.Name
    $ObjectClass = $_.ObjectClass
    $ObjectGUID = $_.ObjectGUID
    $SamAccountName = $_.SamAccountName
    $SID = $_.SID
    $Surname = $_.Surname
    $UserPrincipalName = $_.UserPrincipalName
    $nTSecurityDescriptor = $_.nTSecurityDescriptor
    
    
    $Array += New-Object psobject -Property @{
        DistinguishedName = $DistinguishedName
        GivenName = $GivenName
        Name = $Name
        Owner = $nTSecurityDescriptor.owner
        ObjectClass = $ObjectClass
        ObjectGUID = $ObjectGUID
        SamAccountName = $SamAccountName
        SID = $SID
        Surname = $Surname
        UserPrincipalName = $UserPrincipalName
        }
    
    $DistinguishedName = $null
    $GivenName = $null
    $Name = $null
    $nTSecurityDescriptor = $null
    $ObjectClass = $null
    $ObjectGUID = $null
    $SamAccountName = $null
    $SID = $null
    $Surname = $null
    $UserPrincipalName = $null
    $CurrentUser = $null
    $nTSecurityDescriptor = $null
    }

Bon maintenant qu'on a stocké les donnés, il faudrait les exploiter :

# How many Accounts were returns ?
Write-Host $Array.Count -ForegroundColor Yellow

# How many Accounts need to be reviewed ?
$NoGood = $Array.Where({(($_.Owner -ne "$Domain\Domain Admins") -and ($_.Owner -ne "$Domain\Enterprise Admins") -and ($_.Owner -ne "BUILTIN\Administrators"))})
Write-Host $NoGood.count -ForegroundColor Cyan

Apparement j'ai 12 comptes sur les 6031 de mon AD qui n'ont pas "Domain Admins", "Enterprise Admins" ou "Administrators" comme Owner, voyons voir cela.

$NoGood | ft Name,Owner

Ah, attention on voit bien que qu'il y a des objets liés à Exchange, je vais donc ajouter un filtre.

$Filter = $NoGood.Where({$_.Name -notlike "HealthMailbox*"})

Et voilà, je n'ai plus qu'un objet, super.

Il est possible d'appliquer la même mécanique aux éléments ci-dessous en modifiant la requête :

  • Unités d'Organisation
  • Groupes
  • Ordinateurs

 Unités d'Organisation

# Create Array
$Array = @()

# Collect AD infos
$Domain = Get-ADDomain | select -ExpandProperty NetBIOSName
$AllOU = Get-ADOrganizationalUnit -Filter * -Properties nTSecurityDescriptor

# Store Info
$AllOU | foreach {
    $City = $_.City
    $Country = $_.Country
    $DistinguishedName = $_.DistinguishedName
    $ManagedBy = $_.ManagedBy
    $Name = $_.Name
    $nTSecurityDescriptor = $_.nTSecurityDescriptor
    
    
    $Array += New-Object psobject -Property @{
        City = $City
        Country = $Country
        DistinguishedName = $DistinguishedName
        ManagedBy = $ManagedBy
        Name = $Name
        Owner = $nTSecurityDescriptor.owner
        }
    
    $City = $null
    $Country = $null
    $DistinguishedName = $null
    $ManagedBy = $null
    $Name = $null
    $nTSecurityDescriptor = $null
    }

# How many Accounts were returns ?
Write-Host $Array.Count -ForegroundColor Yellow

# How many Accounts need to be reviewed ?
$NoGood = $Array.Where({(($_.Owner -ne "$Domain\Domain Admins") -and ($_.Owner -ne "$Domain\Enterprise Admins") -and ($_.Owner -ne "BUILTIN\Administrators"))})
Write-Host $NoGood.count -ForegroundColor Cyan

 

Groupes

# Create Array
$Array = @()

# Collect AD infos
$Domain = Get-ADDomain | select -ExpandProperty NetBIOSName
$AllGroups = Get-ADGroup -Filter * -Properties nTSecurityDescriptor

# Store Info
$AllGroups | foreach {
    $DistinguishedName = $_.DistinguishedName
    $GroupCategory = $_.GroupCategory
    $GroupScope = $_.GroupScope
    $Name = $_.Name
    $ObjectClass = $_.ObjectClass
    $ObjectGUID = $_.ObjectGUID
    $SamAccountName = $_.SamAccountName
    $SID = $_.SID
    $nTSecurityDescriptor = $_.nTSecurityDescriptor
    
    
    $Array += New-Object psobject -Property @{
        DistinguishedName = $DistinguishedName
        GroupCategory = $GroupCategory
        GroupScope = $GroupScope
        Name = $Name
        ObjectClass = $ObjectClass
        ObjectGUID = $ObjectGUID
        SamAccountName = $SamAccountName
        SID = $SID
        Owner = $nTSecurityDescriptor.owner
        }
    
    $DistinguishedName = $null
    $DNSHostName = $null
    $Enabled = $null
    $Name = $null
    $ObjectClass = $null
    $ObjectGUID = $null
    $SamAccountName = $null
    $SID = $null
    $nTSecurityDescriptor = $null
    }

# How many Accounts were returns ?
Write-Host $Array.Count -ForegroundColor Yellow

# How many Accounts need to be reviewed ?
$NoGood = $Array.Where({(($_.Owner -ne "$Domain\Domain Admins") -and ($_.Owner -ne "$Domain\Enterprise Admins") -and ($_.Owner -ne "BUILTIN\Administrators"))})
Write-Host $NoGood.count -ForegroundColor Cyan

 

Ordinateurs

# Collect AD infos
$Domain = Get-ADDomain | select -ExpandProperty NetBIOSName
$AllComputers = Get-ADComputer -Filter * -Properties nTSecurityDescriptor

# Store Info
$AllComputers | foreach {
    $DistinguishedName = $_.DistinguishedName
    $GroupCategory = $_.GroupCategory
    $GroupScope = $_.GroupScope
    $Name = $_.Name
    $ObjectClass = $_.ObjectClass
    $ObjectGUID = $_.ObjectGUID
    $SamAccountName = $_.SamAccountName
    $SID = $_.SID
    $nTSecurityDescriptor = $_.nTSecurityDescriptor
    
    
    $Array += New-Object psobject -Property @{
        DistinguishedName = $DistinguishedName
        DNSHostName = $DNSHostName
        Enabled = $Enabled
        Name = $Name
        ObjectClass = $ObjectClass
        ObjectGUID = $ObjectGUID
        SamAccountName = $SamAccountName
        SID = $SID
        Owner = $nTSecurityDescriptor.owner
        }
    
    $DistinguishedName = $null
    $DNSHostName = $null
    $Enabled = $null
    $Name = $null
    $ObjectClass = $null
    $ObjectGUID = $null
    $SamAccountName = $null
    $SID = $null
    $nTSecurityDescriptor = $null
    }

# How many Accounts were returns ?
Write-Host $Array.Count -ForegroundColor Yellow

# How many Accounts need to be reviewed ?
$NoGood = $Array.Where({(($_.Owner -ne "$Domain\Domain Admins") -and ($_.Owner -ne "$Domain\Enterprise Admins") -and ($_.Owner -ne "BUILTIN\Administrators"))})
Write-Host $NoGood.count -ForegroundColor Cyan



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