Any idea how to resolve this powershell error? - powershell

Please forgive me on this one.
I am extremely new to power shell.
I posted a question here yesterday asking for any assisting with reading contents of iis log programmatically.
No response on that yet.
So, after painstaking googling, I found a Power Shell script that appears close to what I am looking for.
When I attempted to execute it, I got the following error:
Get-ChildItem : Cannot find path '\\servername\c$\windows\system32\logfiles\
W3SVC1' because it does not exist.
I am reasonably confident that this is more of a permission issue because the path exists.
Does anyone know how to add username and password to the script below?
Thanks a lot in advance.
#
# Script constants
#
$Server = 'myServer'
$Days = 1
Function Read-IISLog {
[CmdLetBinding()]
Param(
[Parameter(ValueFromPipelineByPropertyName = $True)]
[ValidateScript( { $_ | %{ Test-Path $_ } } )]
[Alias("FullName")]
[String[]]$Path
)
Process {
$Path | ForEach-Object {
$FullPath = (Get-Item $_).FullName
$FileStream = New-Object IO.FileStream($FullPath, "Open", "Read", "ReadWrite")
$StreamReader = New-Object IO.StreamReader($FileStream)
For ($i = 0; $i -lt 4; $i++) {
$Line = $StreamReader.ReadLine()
If ($Line -Match '#Fields: ') {
$Header = ($Line -Replace '#Fields: ').Split(' ', [StringSplitOptions]::RemoveEmptyEntries)
}
}
Do {
$Line = $StreamReader.ReadLine()
If ($Line -NotLike "#*") {
$Line | ConvertFrom-Csv -Delimiter ' ' -Header $Header
}
} Until ($StreamReader.EndOfStream)
}
}
}
$Output = #{}
$Server | ForEach-Object {
Get-ChildItem "\\$_\c$\windows\system32\logfiles\W3SVC1" -Filter *.log |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-$Days) } |
Read-IISLog |
Where-Object { $_."cs-uri-stem" -Match 'ActiveSync' }
} | Select-Object `
#{n='FirstSync';e={ [DateTime]::ParseExact("$($_.date) $($_.time)", "yyyy-MM-dd HH:mm:ss", $Null) }},
#{n='LastSync';e={ [DateTime]::ParseExact("$($_.date) $($_.time)", "yyyy-MM-dd HH:mm:ss", $Null) }},
#{n='ClientIP';e={ [Net.IPAddress]($_."c-ip") }},
#{n='ClientDevice';e={ $_."cs(User-Agent)" }},
#{n='AuthenticatedUser';e={ $_."cs-username" }},
#{n='Mailbox';e={ $_."cs-uri-stem".Split("/")[2] }},
#{n='DeviceId';e={ $_."cs-uri-stem".Split("/")[-1] }},
#{n='DeviceType';e={ $_."cs-uri-stem".Split("/")[-2] }},
#{n='SyncCount';e={ 1 }} |
ForEach-Object {
If (!$Output.Contains($_.DeviceId)) {
$Output.Add($_.DeviceId, $_)
} Else {
If ($_.FirstSync -lt $Output[$_.DeviceId].FirstSync) { $Output[$_.DeviceId].FirstSync = $_.FirstSync }
If ($_.LastSync -gt $Output[$_.DeviceId].LastSync) { $Output[$_.DeviceId].LastSync = $_.LastSync }
$Output[$_.DeviceId].SyncCount += 1
}
}
$Output.Values
This is a desperate attempt at resolving this as users are expecting a demo tomorrow.

Thank you good people for your assistance.
I found a better solution, one that I can manage better - LogParser:
https://mlichtenberg.wordpress.com/2011/02/03/log-parser-rocks-more-than-50-examples/

Related

Powershell - Exchange JSON output without needing to write to a file

EDIT: Added Setupconfigfiles.ps1
I'm a bit new to detailed scripting so please bear with me.
I have two Powershell scripts
Setupconfigfiles.ps1 generates JSON output to be fed to an API.
Script2 uses that JSON data to execute API commands.
Script 2 can call setupconfigfiles.ps1 as indicated below and use the output data.
.\SetupConfigFiles.ps1 -type $Type -outfile .\Templist.json
$servers = Get-Content -Raw -Path .\templist.json | ConvertFrom-Json
setupconfigfiles.ps1:
param (
# If this parameter is set, format the output as csv.
# If this parameter is not set, just return the output so that the calling program can use the info
[string]$outfile,
# this parameter can be 'production', 'development' or 'all'
[string]$type
)
enum MachineTypes {
production = 1
development = 2
all = 3
}
$Servers = Get-ADObject -Filter 'ObjectClass -eq "computer"' -SearchBase 'Obfuscated DSN' | Select-Object Name
$output = #()
$count = 0
# Set this to [MachineTypes]::production or [MachineTypes]::development or [MachineTypes]::all
if ($type -eq "all") {
$server_types = [MachineTypes]::all
}
ElseIf ($type -eq "production") {
$server_types = [MachineTypes]::production
}
else {
$server_types = [MachineTypes]::development
}
ForEach ($Server in $Servers)
{
$count = $count + 1
$this_server = #{}
$this_server.hostname = $Server.Name
$this_server.id = $count
$this_server."site code" = $this_server.hostname.substring(1,3)
$this_server."location code" = $this_server.hostname.substring(4,2)
if ($this_server.hostname.substring(7,1) -eq "P") {
$this_server.environment = "Production"
}
ElseIf ($this_server.hostname.substring(7,1) -eq "D") {
$this_server.environment = "Development"
}
Else {
$this_server.environment = "Unknown"
}
if (($server_types -eq [MachineTypes]::production ) -and ($this_server.environment -eq "Production")) {
$output += $this_server
}
ElseIf (($server_types -eq [MachineTypes]::development ) -and ($this_server.environment -eq "Development")) {
$output += $this_server
}
Else {
if ($server_types -eq [MachineTypes]::all ) {
$output += $this_server
}
}
}
if ($outfile -eq "")
{
ConvertTo-Json $output
}
else {
ConvertTo-Json $output | Out-File $outfile
}
How can I do it without needing to write to the Templist.json file?
I've called this many different ways. The one I thought would work is .\SetupConfigFiles.ps1 $servers
Y'all are great. #Zett42 pointed me in a direction and #Mathias rounded it out.
The solution was to change:
"ConvertTo-Json $output" to "Write-Output $output"
Then it's handled in the calling script.
thanks!

Powershell.I want to add a object to my array $computers but it said that the index is out of bounds

I want to add a object to my array $computers
but it said that the index is out of bounds
function ajouterperipherique ($id, $emplacement, $type) {
$computers = import-csv -path "C:\Temp\Peripherique.csv"
echo $computers
$newObject = [pscustomobject]#{
idObject = $id
EmplacementObject = $emplacement
TypeObject=$type
}
for ($i = 0; $i -lt $computers.Count; $i++) {
if ($i +1 -eq $computers.count) {
$computers[$computers.count+1]=$newObject
}
}
Write-Host ($newObject | Format-List | Out-String)
}
ajouterperipherique "GLADIATOR" "ordinateur" "Statique"
Here is the solution proposed by Lee_Dailey:
$csvPath='C:\Temp\Peripherique.csv'
function ajouterperipherique {
param(
[string]$ID,
[string]$Emplacement,
[string]$Type,
[string]$Path
)
if(-not(Test-Path $Path) -or -not [IO.Path]::GetExtension($Path) -eq '.csv')
{
throw 'File doest not exist or is not a Csv...'
}
[pscustomobject]#{
Identifiant = $id
Type = $type
Emplacement = $emplacement
}|Export-Csv $Path -NoTypeInformation -Append
}
ajouterperipherique -ID "GLADIATOR" -Emplacement "ordinateur" -Type "Statique" -Path $csvPath
A few tips, as pointed out in comments, you shouldn't really use or you should try to avoid Write-Host whenever possible.
You shouldn't really hardcode paths inside your functions, since they're meant to be re-used, hardcoding information you know can change in the future is never a good idea.
You might also wanna consider setting your parameters as Mandatory, parameters are somewhat important in Powershell and can make your life easier. I recommend reading this article if you're thinking of creating more functions in the future: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_advanced_parameters?view=powershell-7.1

Powershell Return only TRUE if All Values are the same

I have the script below to read registry values from a certain key(taking no credit for it). My end goal is to only return TRUE if all the values in the array Match. However I'm not quite getting it as
Example Registry Entry
$array = #()
$regval = Get-Item -Path HKLM:\SOFTWARE\Runner\Event
$regval.GetValueNames() |
ForEach-Object {
$name = $_
$rv.Value
$array += New-Object psobject -Property #{'Value' = $rv.Value }
}
$Matchvalue = 'A'
Foreach ($v in $array){
if ($v -match $Matchvalue){
$true
}
}
Update: I've just tried again and it appears my array is empty. So any tips welcome for me.
How about this:
$regkey = Get-Item HKLM:\SOFTWARE\Runner\Event
$matchPattern = 'A'
$values = $regkey.GetValueNames()
$matchingValues = $values | Where { $regkey.GetValue($_) -match $matchPattern }
# this is going to be true or false
$values.Count -eq $matchingValues.Count
Note that by default, Powershell is case-insensitive. So $matchPattern = 'A' and $matchPattern = 'a' will behave the same.
Here's my attempt to do something like Haskell's "all".
function foldr ($sb, $accum, $list) {
if ($list.count -eq 0) { $accum }
else { & $sb $list[0] (foldr $sb $accum $list[1..$list.length]) }
}
function and ($list) {
foldr { $args[0] -and $args[1] } $true $list
}
function all ($list, $sb) {
and ( $list | foreach $sb )
}
all 1,1,1 { $_ -eq 1 }
True
all 1,2,1 { $_ -eq 1 }
False

Writing an output on a .txt file on Powershell

I found a little script to get all the local groups and members and it's working perfectly but I need to write the output on PowerShell.
Trap {"Error: $_"; Break;}
function EnumLocalGroup($LocalGroup) {
$Group = [ADSI]"WinNT://$strComputer/$LocalGroup,group"
"`r`n" + "Group: $LocalGroup"
$Members = #($Group.psbase.Invoke("Members"))
foreach ($Member In $Members) {
$Name = $Member.GetType().InvokeMember("Name", 'GetProperty', $Null, $Member, $Null)
$Name
}
}
$strComputer = gc env:computername
"Computer: $strComputer"
$computer = [adsi]"WinNT://$strComputer"
$objCount = ($computer.PSBase.Children | Measure-Object).Count
$i = 0
foreach ($adsiObj in $computer.PSBase.Children) {
switch -regex ($adsiObj.PSBase.SchemaClassName) {
"group" {
$group = $adsiObj.Name
EnumLocalGroup $group
}
}
$i++
}
I already tried this:
function EnumLocalGroup($LocalGroup) | Out-File -FilePath "E:\PS\Malik\group.txt"
But the code won't start if I do that. I also tried to use this whole Out-File line at the end of the code after the } but doesn't work either and this is the only solution I find on Internet.
If you want to incorporate logging into a function you need to put it into the function body, e.g.
function EnumLocalGroup($LocalGroup) {
....
$foo = 'something'
$foo # output returned by function
$foo | Add-Content 'log.txt' # output to log file
...
}
or
function EnumLocalGroup($LocalGroup) {
...
$foo = 'something'
$foo | Tee-Object 'log.txt' -Append # output goes to log file and StdOut
...
}
Otherwise you have to do the logging when you call the function:
EnumLocalGroup $group | Add-Content 'C:\log.txt'

reconstructing path from outlined directory structure

I have a csv file in the form:
Address,L0,L1,L2,L3,L4
01,Species,,,,
01.01,,Mammals,,,
01.01.01,,,Threatened,,
...
I want to use it to create a matching directory structure. I'm new to scripting and PowerShell, and in this case I'm not sure if I'm on totally the wrong track. Should I use a separate array to store each level's Name/Address pairs and then use those arrays like a lookup table to build the path? If so, I guess I'm stuck on how to set up if-thens based on a row's Address. This is as far as I've got so suggestions on general strategy or links to similar kinds of problem would be really welcome:
$folders = Import-Csv "E:\path\to\file.csv"
$folders | foreach {
$row = new-object PSObject -Property #{
Address = $_.Address;
Level = ([regex]::Matches($_.Address, "\." )).count;
L0 = $_.L0
L1 = $_.L1
L2 = $_.L2
L3 = $_.L3
}
$array += $row
}
#top level directories
$0 = $array | ?{$_.Level -eq 0} |
Select-Object #{n="Address";e={$_.Address;}},#{n="Name";e={$_.L0}}
#2nd level directories
$1 = $array | ?{$_.Level -eq 1} |
Select-Object #{n="Number";e={$_.Address.split(".")[-1];}},#{n="Name";e={$_.L1}}
Not tested, but I think this might do what you want:
$root = 'E:\SomeDirName'
Switch -Regex (Get-Content "E:\path\to\file.csv")
{
'^01,(\w+),,,,$' { $L1,$L2,$L3,$L4 = $null; $L0=$matches[1];mkdir "$root\$L0" }
'^01\.01,,(\w+),,,$' { $L1=$matches[1];mkdir "$root\$L0\$L1" }
'^01\.01\.01,,,(\w+),,$' { $L2=$matches[1];mkdir "$root\$L0\$L1\$L2" }
'^01\.01\.01\.01,,,,(\w+),$' { $L3=$matches[1];mkdir "$root\$L0\$L1\$L2\$L3" }
'^01\.01\.01\.01\.01,,,,,(\w+)$' { $L4=$matches[1];mkdir "$root\$L0\$L1\$L2\$L3\$L4" }
}
To solve that kind of problem a programming concept called recursion is often used.
In short a recursive function is a function that call itself.
I successfully tested this code with you CSV input:
$csvPath = 'C:\Temp\test.csv'
$folderRoot = 'C:\Temp'
$csv = Import-Csv $csvPath -Delimiter ',' | Sort-Object -Property Address
# Recursive function
function Recurse-Folder( $folderAddress, $basePath )
{
# Getting the list of current folder subfolders
$childFolders = $null
if( $folderAddress -like '' )
{
$childFolders = $csv | Where-Object { $_.Address -like '??' }
}
else
{
$childFolders = $csv | Where-Object { $_.Address -like $( $folderAddress + '.??' ) }
}
# For each child folder
foreach( $childFolder in $childFolders )
{
# Get the folder name
$dotCount = $childFolder.Address.Split('.').Count - 1
$childFolderName = $childFolder.$('L'+$dotCount)
if( $childFolderName -ne '')
{
$childFolderPath = $basePath + '\' + $childFolderName
# Creating child folder and calling recursive function for it
New-Item -Path $childFolderPath -ItemType Directory
Recurse-Folder $childFolder.Address $childFolderPath
}
}
}
Recurse-Folder '' $folderRoot