Acredito que seu problema gira em torno do fato de que você está escrevendo vários tipos diferentes de objetos no pipeline de saída (implicitamente não atribuindo-os a uma variável) e confiando nas regras de formatação padrão do Powershell para exibi-las adequadamente no console ( o que não pode).
Ele está exibindo o primeiro tipo de objeto e, em seguida, tentando exibir o segundo tipo de objeto usando o mesmo formatador que o primeiro e apenas escrevendo linhas em branco. Você teria o problema oposto se mover sua chamada Get-ADUser abaixo do loop com as chamadas Get-ADComputer.
Supondo que a saída desse script tenha sempre a intenção de ser exibida no console e não ser processada posteriormente por outros scripts ou encadeada com outros cmdlets, convém usar Write-Host
com formatação explícita para cada parte de sua saída. Normalmente, esta é uma má ideia embora . Algo parecido com isto.
$user = Get-ADUser -Identity $username –Properties “DisplayName”, “msDS-UserPasswordExpiryTimeComputed”, "LockedOut"
Write-Host "User: $($user.DisplayName)'tPWD Expiration: $([datetime]::FromFileTime($_.“msDS-UserPasswordExpiryTimeComputed”))'tLockedOut: $($user.LockedOut)"
Também gostaria de oferecer alguns conselhos adicionais sobre a estrutura geral do seu script.
Primeiro, eu tornaria sua variável $username
um parâmetro obrigatório em vez de chamar explicitamente Read-Host
. O Powershell solicitará automaticamente se não estiver especificado na linha de comando.
Ao verificar a existência do usere, você está alterando o tipo de objeto de sua variável $username
de uma string para um objeto ADUser
. Eu estou supondo que isso não foi sua intenção e é apenas uma sorte que as referências posteriores trabalhem usando o objeto em vez da string. Você também faz uma segunda chamada Get-ADUser
logo depois para obter os atributos de seu interesse, o que custa uma ida e volta extra ao controlador de domínio. Eu apenas combinei os dois e movi sua seção ## Get Username info
para o bloco try / catch. Ele ainda lançará a mesma exceção se o usuário não existir. Eu também atribuiria a saída a uma nova variável como $user
.
Opcionalmente, você também pode ignorar todo o tratamento de exceções e alterar a chamada Get-ADUser
para usar o parâmetro -Filter {SamAccountName -eq $username}
e, em seguida, testar apenas para $ null, como acontece mais tarde com Get-ADComputer
.
Você também acaba duplicando suas chamadas para Get-ADComputer. Primeiro você está chamando com o filtro e, em seguida, está chamando novamente cada resultado. Seria mais eficiente apenas adicionar os atributos DNSHostName,LastLogonTimestamp
à chamada inicial e depois iterar diretamente sobre os objetos resultantes sem a necessidade de chamar novamente o Get-ADComputer.
Veja como um script resultante pode parecer:
param(
[Parameter(Mandatory=$true,Position=0)]
[string]$username
)
# Import AD Module
Import-Module ActiveDirectory
Write-Host "Active Domain: $((Get-ADDomain).DNSRoot)"
# Get user details
$user = Get-ADUser -Filter {SamAccountName -eq $username} -Properties DisplayName,msDS-UserPasswordExpiryTimeComputed,LockedOut
If ($user -eq $null) {
Write-Error "Error: Username not found. Exiting"
Exit
}
Write-Host "Name: $($user.DisplayName)"
Write-Host "PWD Expiration Timestamp: $([datetime]::FromFileTime($_.'msDS-UserPasswordExpiryTimeComputed'))"
Write-Host "LockedOut: $($user.LockedOut)"
# Get managed computers
$ComputerList = Get-ADComputer -Filter {ManagedBy -eq $username} -Properties ManagedBy,DNSHostName,LastLogonTimestamp
# Error-Handling: If no computers found
If ($ComputerList -eq $null) {
Write-Error "No computers found"
Exit
}
# Compute and format results
ForEach ($computer in $ComputerList) {
$OnlineStatus = Test-Connection -ComputerName $computer.Name -BufferSize 16 -Count 1 -Quiet
# since this is now the only type of object we will be putting on the pipeline,
# it's ok to just do that now
$computer | Select-Object DNSHostName,@{L="Active";E={$OnlineStatus}},@{L="LastLogonTimestamp";E={[datetime]::FromFileTime($_.LastLogonTimestamp)}} | Write-Output
}