Better than Switch case in powershel? - azure

I have an array like 5 system IDs for now like SID_1_name, SID_2_name......For each system ID .I need to add some details. So I have written powershell script of switch case .Which is working fine for now. But I wonder in future we may add more systems so that I need to add more cases which expands the script...Is there better way to do for future purpose aswell
$excel=#()
$list = Get-AznetworkInterface |where-Object {$_.ResourceGroupName -Clike '*$(givenVarible)'} |Select-Object
foreach ($i in $list) {
$x = " " | Select-Object SID1_name,SID1_VIP,SID2_name,SID2_VIP,SID3_name,SID3_VIP
$case =1
While ($case -1t $i.IpConfigurations.Count)
{
switch ($case){
1 {
$x.SID1_name = $i.IPconfigurations[$case];
$x.SID1_VIP = $i.IPconfigurations[$case].PrivateIpaddress;
break
}
2 {
$x.SID2_name = $i.IPconfigurations[$case];
$x.SID2_VIP = $i.IPconfigurations[$case].PrivateIpaddress;
break
}
3 {
$x.SID1_name = $i.IPconfigurations[$case];
$x.SID1_VIP = $i.IPconfigurations[$case].PrivateIpaddress;
break
}
$case =$case+1
$excel +=$x
$excel | Format-Table SID1_name,SID1_VIP,SID2_name,SID2_VIP,SID3_name,SID3_VIP
$excel |Export-Csv -NTI - Path "$(Build.ArtifactoryStagingDirectory)/report.csv"

Related

How to modify excel data and export to text file using PowerShell script?

First time poster here. Apologies if I am not following best practices for posting this question.
I am very new to scripting and PowerShell.
Problem:
I have data in an excel sheet in this format.
Excel Data Image Link
I want to modify and export this data into a text file. In this format.
Required Output Image Link
Till now I have tried to modify the excel data by accessing each cell. To access each cell I am using a similar code mentioned below.
for (($i = 1); $i -lt 4; $i++)
{
$column=$ExcelWorkSheet.Columns.Item(1).Rows.Item($i).Text
$dataType=$ExcelWorkSheet.Columns.Item(2).Rows.Item($i).Text
$c1=("`"" + "$column" + "`""+":")
$c2=("`"" + "$dataType" + "`"" + ",")
$ExcelWorkSheet.Columns.Item(1).Rows.Item($i).Value=$c1
$ExcelWorkSheet.Columns.Item(2).Rows.Item($i).Value=$c2
}
I am still not sure if this is the correct way to go.
what would be the best way to solve this?
Just want to understand what I should do to solve this problem. I am not looking for the exact code.
Step by step instructions or some resources would be helpful.
Thanks!
This might help... maybe...
# Import Stuff
$Data = Import-Csv -Path .\Desktop\data.csv
# New Array
$Output = #()
# Run through Unique Owners
foreach ($Owner in ($Data | Select-Object OWNER -Unique)) {
$Lines = $Data | Where-Object {$_.OWNER -eq $Owner.OWNER}
# Lazy way to do a bit of checking, if same then use it or Break
if ($Lines[0].TABLE_NAME -eq $Lines[1].TABLE_NAME) {
$Out_TableName = $Lines[0].TABLE_NAME
# ID and NAME data
$Out_ID = $Lines | Where-Object {$_.COLUMN_NAME -eq "ID"} | Select-Object COLUMN_NAME, DATA_TYPE, DATA_LENGTH
$Out_NAME = $Lines | Where-Object {$_.COLUMN_NAME -eq "NAME"} | Select-Object COLUMN_NAME, DATA_TYPE, DATA_LENGTH
} else {
# Show the user that something
Write-Host "Problem with Owner ""$($Owner.OWNER)"" Data?!" -ForegroundColor Red
Break
}
# Output into the array in format
$Output += #"
"$($Owner.OWNER).$($Out_TableName)":{
"$($Out_ID.COLUMN_NAME)": "$($Out_ID.DATA_TYPE) ($($Out_ID.DATA_LENGTH))",
"$($Out_NAME.COLUMN_NAME)": "$($Out_NAME.DATA_TYPE) ($($Out_NAME.DATA_LENGTH))"
}
"#
}
# Put Output in a text file
$Output | Set-Content .\Desktop\output.txt -Force
I should add, that I had your data in a CSV like this...
OWNER,TABLE_NAME,COLUMN_NAME,DATA_TYPE,DATA_LENGTH
A,Employee,ID,NUMBER,22
A,Employee,NAME,VARCHAR2,22
B,Department,ID,NUMBER,23
B,Department,NAME,VARCHAR2,24

Two objects of different sizes and $null

My code below works in every instance except for if one object is $null and the other object has one item. When that situation occurs the output becomes 1 letter like it is indexing and I am not sure why.
How do I combine the two objects to make a final report?
$ADGroups = Get-ADPrincipalGroupMembership -Identity $UserSam | Select-Object distinguishedName, name | Where-Object { ($_.distinguishedName -ne 'CN=Domain Users,CN=Users,DC=com') }
#record AD groups
$ADResult = #()
if ($null -eq $ADGroups) {
Write-Warning "No AD Groups"
$ADResult = [PSCustomObject]#{
ADGroups = #()
ADGroupsdistinguishedName = #()
}
}
Else {
$ADResult = $ADGroups | ForEach-Object {
[PSCustomObject]#{
ADGroups = $_.name
ADGroupsdistinguishedName = $_.distinguishedName
}
}
}
#============= Now Google, get user groups and record
$GoogleGroups = gam print groups member $email members managers owners | ConvertFrom-Csv
# Record Google Groups
$GResult = #()
If ($null -eq $GoogleGroups) {
Write-Warning "No Google Groups"
$GResult = [PSCustomObject]#{
GoogleGroups = #()
Role = #()
}
}
Else {
$group = $null
$GResult = ForEach ($group in $GoogleGroups) {
#this records what role the user had in the group(s)
$GoogleMember = gam print group-members group $group.email members | ConvertFrom-Csv | Select-Object -ExpandProperty email
$Role = $null
If ( $GoogleMember -contains $EMAIL) {
$Role = 'Member'
}
Else {
$GoogleManager = gam print group-members group $group.email managers | ConvertFrom-Csv | Select-Object -ExpandProperty email
If ($GoogleManager -contains $EMAIL) {
$Role = 'Manager'
}
Else {
$Role = 'Owner'
}
}
[PSCustomObject]#{
GoogleGroups = $group.email
Role = $role
}
$group = $null
}
}
# ---------now report that will be dropped off at end
[int]$max = $ADResult.count
if ([int]$GResult.count -gt $max) { [int]$max = $GResult.count }
If ($max -eq 1 -or $max -eq 0) {
$Result = [PSCustomObject]#{
PrimaryEmail = $email
Title = $UserInfo.title
Department = $UserInfo.Department
Manager = $Manager
ADGroupName = $ADResult.ADGroups
ADGroupNameDistinguishedName = $ADResult.ADGroupsdistinguishedName
GoogleGroup = $GResult.GoogleGroups
Role = $GResult.role
DateOfSeparation = (Get-Date).ToString("yyyy_MM_dd")
UserDistinguishedName = $UserInfo.distinguishedName
UserOU = $UserInfo.Ou
PrimaryGroup = $UserInfo.primaryGroup.Split('=').Split(',')
}
}
Else {
$Result = for ( $i = 0; $i -lt $max; $i++) {
[PSCustomObject]#{
PrimaryEmail = $email
Title = $UserInfo.title
Department = $UserInfo.Department
ADGroupName = $ADResult.ADGroups[$i]
ADGroupNameDistinguishedName = $ADResult.ADGroupsdistinguishedName[$i]
GoogleGroup = $GResult.GoogleGroups[$i]
Role = $GResult.role[$i]
DateOfSeparation = (Get-Date).ToString("yyyy_MM_dd")
UserDistinguishedName = $UserInfo.distinguishedName
UserOU = $UserInfo.Ou
PrimaryGroup = $UserInfo.primaryGroup.Split('=').Split(',')[$i]
}
}
}
$Result | Export-Csv 'C:\temp\Groups.csv' -NoTypeInformation
Going by the abstract description of your problem:
You're seeing an unfortunate asymmetry in PowerShell:
In the pipeline, a [string] instance is considered a single object.
PS> ('foo' | Measure-Object).Count
1
With respect to indexing, it is considered an array of characters.
PS> 'foo'[0]
f
A general feature of capturing a PowerShell pipeline's output is that if a command situationally outputs just a single object, that object is captured as-is, whereas two or more output objects result in a regular PowerShell array, of type [object[]].
Typically, this isn't a problem, because PowerShell's unified handling of scalars and collections allows you to index even into a scalar (single object), i.e. to implicitly treat a single object as if it were a single-element array:
PS> (Write-Output 42, 43)[0]
42
PS> (Write-Output 42)[0]
42 # still OK, even though only *one* object was output; same as: (42)[0]
However, with a single [string] instance as the output it becomes a problem, for the reasons stated above:
PS> (Write-Output 'foo', 'bar')[0]
foo # OK
PS> (Write-Output 'foo')[0]
f # !! Indexing into a *single string* treats it as *character array*
The same applies to values returned via member-access enumeration, perhaps surprisingly :
PS> (Get-Item $HOME, /).FullName[0]
C:\Users\Jdoe
PS> (Get-Item $HOME).FullName[0]
C # !! Indexing into a *single string* treats it as *character array*
Workarounds:
Enclose the command of interest in #(...), the array-subexpression operator so as to ensure that its output is always considered an array.
PS> #(Write-Output 'foo')[0]
foo # OK
Alternatively, when capturing a command's output in a variable, type-constrain that variable to [array] (same as [object[]]) or a strongly typed array, [string[]]:
PS> [array] $output = Write-Output 'foo'; $output[0]
foo # OK

PowerShell script to monitor IIS logs for 500 errors every 10 minutes

I'm trying to set up a script to monitor IIS 7.5 logs fro 500 errors. Now I can get it to do that OK but I would like it to check every 30 minutes. Quite naturally I don't want it to warn me about the previous 500 errors it has already reported.
As you can see from the script below I have added a $time variable to take this into account, however I can't seem to find a way to use this variable. Any help would be appreciated.
#Set Time Variable -30
$time = (Get-Date -Format hh:mm:ss (Get-Date).addminutes(-30))
# Location of IIS LogFile
$File = "C:\Users\here\Documents\IIS-log\"+"u_ex"+(get-date).ToString("yyMMdd")+".log"
# Get-Content gets the file, pipe to Where-Object and skip the first 3 lines.
$Log = Get-Content $File | where {$_ -notLike "#[D,S-V]*" }
# Replace unwanted text in the line containing the columns.
$Columns = (($Log[0].TrimEnd()) -replace "#Fields: ", "" -replace "-","" -replace "\(","" -replace "\)","").Split(" ")
# Count available Columns, used later
$Count = $Columns.Length
# Strip out the other rows that contain the header (happens on iisreset)
$Rows = $Log | where {$_ -like "*500 0 0*"}
# Create an instance of a System.Data.DataTable
#Set-Variable -Name IISLog -Scope Global
$IISLog = New-Object System.Data.DataTable "IISLog"
# Loop through each Column, create a new column through Data.DataColumn and add it to the DataTable
foreach ($Column in $Columns) {
$NewColumn = New-Object System.Data.DataColumn $Column, ([string])
$IISLog.Columns.Add($NewColumn)
}
# Loop Through each Row and add the Rows.
foreach ($Row in $Rows) {
$Row = $Row.Split(" ")
$AddRow = $IISLog.newrow()
for($i=0;$i -lt $Count; $i++) {
$ColumnName = $Columns[$i]
$AddRow.$ColumnName = $Row[$i]
}
$IISLog.Rows.Add($AddRow)
}
$IISLog | select time,csuristem,scstatus
OK With KevinD's help and PowerGUI with a fair bit of trial and error, I got it working as I expected. Here's the finished product.
#Set Time Variable -30
$time = (Get-Date -Format "HH:mm:ss"(Get-Date).addminutes(-30))
# Location of IIS LogFile
$File = "C:\Users\here\Documents\IIS-log\"+"u_ex"+(get-date).ToString("yyMMdd")+".log"
# Get-Content gets the file, pipe to Where-Object and skip the first 3 lines.
$Log = Get-Content $File | where {$_ -notLike "#[D,S-V]*" }
# Replace unwanted text in the line containing the columns.
$Columns = (($Log[0].TrimEnd()) -replace "#Fields: ", "" -replace "-","" -replace "\(","" -replace "\)","").Split(" ")
# Count available Columns, used later
$Count = $Columns.Length
# Strip out the other rows that contain the header (happens on iisreset)
$Rows = $Log | where {$_ -like "*500 0 0*"}
# Create an instance of a System.Data.DataTable
#Set-Variable -Name IISLog -Scope Global
$IISLog = New-Object System.Data.DataTable "IISLog"
# Loop through each Column, create a new column through Data.DataColumn and add it to the DataTable
foreach ($Column in $Columns) {
$NewColumn = New-Object System.Data.DataColumn $Column, ([string])
$IISLog.Columns.Add($NewColumn)
}
# Loop Through each Row and add the Rows.
foreach ($Row in $Rows) {
$Row = $Row.Split(" ")
$AddRow = $IISLog.newrow()
for($i=0;$i -lt $Count; $i++) {
$ColumnName = $Columns[$i]
$AddRow.$ColumnName = $Row[$i]
}
$IISLog.Rows.Add($AddRow)
}
$IISLog | select #{n="Time"; e={Get-Date -Format "HH:mm:ss"("$($_.time)")}},csuristem,scstatus | ? { $_.time -ge $time }
Thanks again Kev you're a good man. Hope this code helps someone else out there.
Here's
Try changing your last line to:
$IISLog | select #{n="DateTime"; e={Get-Date ("$($_.date) $($_.time)")}},csuristem,scstatus | ? { $_.DateTime -ge $time }
In the select, we're concatenating the date and time fields, and converting them to a date object, then selecting rows where this field is greater than your $time variable.
You'll also need to change your $time variable:
$time = (Get-Date).AddMinutes(-30)
You want a DateTime object here, not a string.

PowerShell: retrieve number of applications in AppPool

How to retrieve the number of applications associated with a specific IIS AppPool via PowerShell command?
We can see the associated applications manually using:
Get-Item IIS:\AppPools\AppPoolName
However, if we manually want to select the Applications column, it is not possible. Also, the Applications column is not listed within | Get-Member *.
Why is the column not listed?
How to find the number of applications associated with a specific IIS AppPool using PowerShell?
The trick is: PowerShell established so-called "view definition files" which tell PowerShell how to format objects (e.g. whether the object is formatted as a a list or a table, which columns are displayed, etc.). Those files can be found at C:\Windows\System32\WindowsPowerShell\v1.0 and are all ending in .format.ps1xml.
To answer the original question: The file C:\Windows\System32\WindowsPowerShell\v1.0\Modules\WebAdministration\iisprovider.format.ps1xml contains the view definition for the AppPool type which defines a calculated column looking like this:
<TableColumnItem>
<ScriptBlock>
$pn = $_.Name
$sites = get-webconfigurationproperty "/system.applicationHost/sites/site/application[#applicationPool=`'$pn`'and #path='/']/parent::*" machine/webroot/apphost -name name
$apps = get-webconfigurationproperty "/system.applicationHost/sites/site/application[#applicationPool=`'$pn`'and #path!='/']" machine/webroot/apphost -name path
$arr = #()
if ($sites -ne $null) {$arr += $sites}
if ($apps -ne $null) {$arr += $apps}
if ($arr.Length -gt 0) {
$out = ""
foreach ($s in $arr) {$out += $s.Value + "`n"}
$out.Substring(0, $out.Length - 1)
}
</ScriptBlock>
</TableColumnItem>
This answers why the column itself is not a member of the AppPool type. The second question can be easily answered now extracting the necessary code from the "scriptlet" above:
$applicationsInAppPoolCount = #(Get-WebConfigurationProperty `"/system.applicationHost/sites/site/application[#applicationPool=`'$appPool`'and #path!='/']"` "machine/webroot/apphost" -name path).Count
I dealt with this same issue for many hours until finally arriving at the solution. The answer from D.R. was very helpful but it was not working for me. After some tweaks, I came up with the code below which retrieves the number of applications in an app pool.
I noticed that this part of the code nd #path!='/' threw off the count.
$appPool = "REPLACE ME with a value from your app pool"
#(Get-WebConfigurationProperty "/system.applicationHost/sites/site/application[#applicationPool=`'$appPool`']" "machine/webroot/apphost" -name path).Count
I ended up with the following Code (basically the same as above, but differently formatted)
$appPools = Get-ChildItem –Path IIS:\AppPools
foreach ($apppool in $apppools) {
$appoolName = $apppool.Name
[string] $NumberOfApplications = (Get-WebConfigurationProperty "/system.applicationHost/sites/site/application[#applicationPool='$appoolName']" "machine/webroot/apphost" -name path).Count
Write-Output "AppPool name: $appoolName has $NumberOfApplications applications"
}
I recently came across this post searching for ways to get the active Application Pools. The information provided above was great, but I kept digging to see if there was another way get this information. I was able to find a way to do this through Get-IISSite, which I used the following:
Get-IISSite | Select-Object -ExpandProperty Applications | Select-Object Path,ApplicationPoolName
I tested this on a server that only had one website, but if there are multiple sites on the server, you could also add VirtualDirectories for the Select.
I also had a need to just get a unique list of the Application Pools being used, so I did the following:
$appPoolInfo = Get-IISSite | Select-Object -ExpandProperty Applications | Select-Object Path,ApplicationPoolName
$appPoolInfo | Select-Object -Unique ApplicationPoolName
This gives what you are looking in an array.
Import-Module WebAdministration;
Get-ChildItem IIS:\AppPools >> AppPoolDetails.txt;
$appPoolDetails = Get-Content .\AppPoolDetails.txt;
$w = ($appPoolDetails |Select-String 'State').ToString().IndexOf("State");
$w = $w -1;
$res1 = $appPoolDetails | Foreach {
$i=0;
$c=0; `
while($i+$w -lt $_.length -and $c++ -lt 1) {
$_.Substring($i,$w);$i=$i+$w-1}}
Write-Host "First Column---";
$res1.Trim();
$j = $w + 1;
$w = ($appPoolDetails |Select-String 'Applications').ToString().IndexOf("Applications");
$w = $w -$j;
$res2 = $appPoolDetails | Foreach {
$i=$j;
$c=0; `
while($i+$w -lt $_.length -and $c++ -lt 1) {
$_.Substring($i,$w);$i=$i+$w-1}}
Write-Host "Second Column---";
$res2.Trim();
$lineLength=0
$appPoolDetails | Foreach {
if($lineLength -lt $_.TrimEnd().Length )
{
$lineLength = $_.TrimEnd().Length;
#Write-Host $lineLength;
}
}
$j = ($appPoolDetails | Select-String 'Applications').ToString().IndexOf("Applications");
$w = $lineLength;
$w = $w -$j;
#Write-Host $j $w;
$res3 = $appPoolDetails | Foreach {
$i=$j;
$c=0; `
while($i+$w -lt $_.length -and $c++ -lt 1) {
$_.Substring($i,$w);$i=$i+$w-1}}
Write-Host "Third Column---";
$res3;

PowerShell - paste data into Excel

Today I have just thrown together this PowerShell script which
takes a tab-delimited text file,
reads it into memory,
makes a variable number of filter queries based on distinct values of a certain column
creates a new empty Excel workbook
adds each of the subsets of filtered data to
a new Excel worksheet
The last step is where I am stuck. Currently my code puts a few lines of data into a range in the worksheet, in the form of unrolled/transposed "key: value" entries, resulting in a horizontal data layout. The same range of data is always overwritten.
I want data in the form of a vertical layout, i.e., data in columns, just the same way as if the CSV file was imported with the import-file-wizard of MS Excel.
Is there a simpler way to do it than below?
I admit, some of the PowerShell features are pasted in here in a cargo-cult mode of programming. Please note that I have no PowerShell experience whatsoever. I did some batchfile, VBScript, and VBA coding a few years back. So, other criticisms are also welcome.
PARAM (
[Parameter(ValueFromPipeline = $true)]
$infile = ".\04-2011\110404-13.txt"
)
PROCESS {
echo " $infile"
Write-Host "Num Args:" $args.Length;
$xl = New-Object -comobject Excel.Application;
$xl.Visible = $true;
$Workbook = $xl.Workbooks.Add();
$content = Import-Csv -delimiter "`t" $infile;
$ports = $content | Select-Object Port# | Sort-Object Port# -Unique -Descending;
$ports | ForEach-Object {
$p = $_;
Write-Host $p.{Port#};
$Worksheet = $Workbook.Worksheets.Add();
$workSheet.Name = [string]::Format("{0} {1}", "PortNo", $p.{Port#});
$filtered = $content | Where-Object {$_.{Port#} -eq $p.{Port#} };
$filtered | ForEach-Object {
Write-Host $_.{ObsDateTime}, $_.{Port#}
}
$filtered | clip.exe;
$range = $Workbook.ActiveSheet.Range("a2", "a$($filtered.count)");
$Workbook.ActiveSheet.Paste($range, $false);
}
$xl.Quit()
}
Data Output Example
Wrong
Port# : 1
Obs# : 1
Exp_Flux : 0,99
IV Cdry : 406.96
IV Tcham : 16.19
IV Pressure : 100.7
IV H2O : 9.748
IV V3 : 11.395
IV V4 : 0.759
IV RH : 53.12
Right
Port# Obs# Exp_Flux IV Cdry IV Tcham IV Pressure IV H2O IV V3 IV V4 IV RH
1 1 0,99 406.96 16.19 100.7 9.748 11.395 0.759 53.12
Try Export-Xls, it looks very nice. Never had the chance to use it, but (virtually) knowing the person who worked on it, I'm sure you will be very happy to use it. If you'll go with it, please provide a feedback here will be appreciated.
POSSIBLE WORKAROUND FOR UNORDERED PROPERTIES IN Export-Xls
The function Add-Array2Clipboard could be changed so that it accepts a new input parameter: an array providing the name of the properties ordered as required.
Then the you can change the section where get-member is used. Silly example:
"z", "a", "c" | %{ get-member -name $_ -inputobject $thecurrentobject }
This is just an example on how you can achieve ordered properties from get-member.
I've used the $Workbook.ActiveSheet.Cells.Item($row, $col).Value2 function to more be able to pinpoint more precisely where to put the data when exporting to Excel.
Something like
$row = 1
Get-Content $file | Foreach-Object {
$cols = $_.split("`t")
for ($i = 0; $i < $cols.count; $i++)
{
$Workbook.ActiveSheet.Cells.Item($row, $i+1).Value2 = $cols[$i]
}
$row++
}
Warning: dry-coded! You'll probably need some try..catch as well.
I used a modified Export-Xls function, a bit different as User empo suggested.
This is my call to it
Export-Xls $filtered -Path $outfile -WorksheetName "$wn" -SheetPosition "end" | Out-Null # -SheetPosition "end";
However, the current release of Export-Xls re-orders the columns of the in-memory representation of the csv-text -file. I want the data columns of the text file in their original order, so I had to hack and simplify the original code as follows:
function Add-Array2Clipboard {
param (
[PSObject[]]$ConvertObject,
[switch]$Header
)
process{
$array = #();
$line =""
if ($Header) {
$line = #()
$row = $ConvertObject | Select -First 1
$row.psobject.properties | Foreach {$line += "$($_.Name)" }
$array += [String]::Join("`t", $line)
}
else {
foreach($row in $ConvertObject){
$line =""
$vals = #()
$row.psobject.properties | Foreach {$vals += $_.Value}
$array += [String]::Join("`t", $vals)
}
}
$array | clip.exe;
}
}

Resources