i got some problem while preparing some useful script based on PS.
I try to make script which will collect data from Active Directory and Exchange (users/mailboxes), then this data will be processed in further part of script (in some function for example)
function toProcess($userObj, $mailboxObj)
{
echo $userObj.enabled #the output is null
}
$users = get-adusers -Filter * -properties *
foreach($user in $users)
{
$guid = $user.objectGuid.toString()
if($user.mail -ne $null)
{
$mailbox = get-mailbox $guid | select-object *
if($mailbox -ne $null)
{
toProcess($user, $mailbox)
}
}
}
When there is assigned only one parameter ($user) to the onProcess(), function executes correctly and display status of account. When i assign two objects, values becomes null.
What's wrong?
I use powershell 2.0
When calling PowerShell functions, arguments are separated by spaces, and parentheses are not needed.
Your function call should look like this.
toProcess $user $mailbox
By placing a comma between the variables, you were creating a single argument that is an array of objects.
Related
This question already has answers here:
How can you use an object's property in a double-quoted string?
(5 answers)
Closed 5 months ago.
I wrote a script that allows me to query the whole Azure database park:
#$ErrorActionPreference = 'SilentlyContinue'
# Connect to Azure
$azureAccount = Connect-AzAccount
# Get Azure Access Token (we will use this to query the databasees)
#$azureToken = Get-AzAccessToken -ResourceUrl https://database.windows.net
$access_token = (Get-AzAccessToken -ResourceUrl https://database.windows.net).Token
# Queries will be picked up from here
$folderPath = '.\Queries'
# Choose how to format each date ("yyyy-MM-dd") or ("yyyy-MM-dd HH:mm:ss")
$DateTime = (Get-Date).ToString("yyyy-MM-dd")
# List Azure Sunscriptions
Get-Azsubscription | ForEach-Object -Begin { $a = 1 } -Process {"$a $($_.Name)"; $a++}
$SubscriptionChoice = Read-Host -Prompt "Copy/paste the name of the Subscription that you want to investigate. If more than one separate them by a coma, Type `"All`" if you want to target all of them"
# Iterate into subscriptoins and print names
foreach ($gs in $SubscriptionChoice) {
Select-Azsubscription -Subscription "$gs" | Out-Null
Write-Host "Let's browse into Azure Sunscription: " -NoNewline
Write-Host (Get-AzContext).Subscription.Name -ForegroundColor green
# Fins all Azure SQL Server
Get-AzSqlServer | ForEach-Object -Begin { $a = 1 } -Process {"$a $($_.ServerName)"; $a++}
$SqlServerChoice = Read-Host -Prompt "Copy/paste the name of the SQL Server that you want to investigate. If more than one separate them by a coma, Type `"All`" if you want to target all of them"
if ($SqlServerChoice = "All"){
$SqlServerChoice = Get-AzSqlServer
}
Foreach ($server in $SqlServerChoice){
$DatabaseChoice = Get-AzSqlDatabase -ServerName $server.ServerName -ResourceGroupName $server.ResourceGroupName | Where-Object DatabaseName -NE "master"
Foreach ($database in $DatabaseChoice){
(Get-ChildItem $folderPath | sort-object {if (($i = $_.BaseName -as [int])) {$i} else {$_}} ).Foreach{
Invoke-Sqlcmd -ServerInstance $server.FullyQualifiedDomainName -Database $database.DatabaseName -AccessToken $access_token -InputFile $psitem.FullName | Export-Csv -Path ".\Results\$psitem.csv" -Append -NoTypeInformation
write-host "Executing $psitem on $database.DatabaseName"
}
}
}
}
However each time the query is executed against a database the Write-Hosts returns:
Executing DTU_to_vCore.sql on Microsoft.Azure.Commands.Sql.Database.Model.AzureSqlDatabaseModel.DatabaseName
Here a picture:
This Write-Hosts comes from the line:
write-host "Executing $psitem on $database.DatabaseName"
In which you can find the two variables:
$psitem : which is the name of the file that contains the query
$database.DatabaseName : which should be the database name but instead of printing the database name is printing Microsoft.Azure.Commands.Sql.Database.Model.AzureSqlDatabaseModel.DatabaseName
Why one of the two variable is not interpreted?
You need to encapsulate your variable property in a subexpression operator $().
write-host "Executing $psitem on $($database.DatabaseName)"
This is because only simple variables get expanded in an expandable string.
References
Only simple variable references can be directly embedded in an
expandable string. Variables references using array indexing or member
access must be enclosed in a subexpression.
Source: about_Quoting_Rules
Subexpression operator $( )
Returns the result of one or more statements. For a single result,
returns a scalar. For multiple results, returns an array. Use this
when you want to use an expression within another expression. For
example, to embed the results of command in a string expression.
PS> "Today is $(Get-Date)"
Today is 12/02/2019 13:15:20
PS> "Folder list: $((dir c:\ -dir).Name -join ', ')"
Folder list: Program Files, Program Files (x86), Users, Windows
Source: about_Operators
Get-ChildItem 'C:\Users\Zac\Downloads\script test\script test\*.txt' -Recurse | ForEach {(Get-Content $_ | ForEach { $_ -replace '1000', $fileNameOnly}) | Set-Content $_ }
I have been trying to use a simple PowerShell script to replace the 1000 value in my documents with the goal of replacing the value with the name of the .nc1/.txt file it is editing.
For example a file that is called BM3333.nc1 has a line value of 1000 which needs to replace it with BM3333 so on, so forth. This will be used in batch editing.
What is the variable that I use for replacing the 1000 with the file name?
So far, I can get this to run but it doesn't replace the 1000 value, it removes it.
Your problem is that inside the ScriptBlock of a ForEach-Object invocation, the variable is $_ (also known as $PSItem). There is no name for the inner script to get the value from the outer script.
You need to create a unique name in the outer script beforehand. The ScriptBlock argument to ForEach-Object does not need to be a single expression. You can either use multiple lines or a ;.
1..3 | ForEach-Object { $a = $_; 100..105 | ForEach-Object { $_ * $a } }
For your use case, you need this variable to be the name of the file. The values in the outer ScriptBlock are System.IO.FileSystemInfo, which were returned by Get-ChildInfo.
PowerShell makes iterating on work like this very easy; try seeing which properties are available:
Get-ChildItem 'C:\Users\Zac\Downloads\script test\script test\*.txt' -Recurse | Select-Object -First 1 | Format-List *
Given:
PowerShell 5.1
Azure DevOps Server 2019
I'm trying to call my function directly from my Azure PowerShell Task Arguments, is this possible? I'm not getting any expected output.
param([String] $Name, [Int] $Age, [String] $Path)
Function Foo
{
Param(
[String]
$Name
,
[Int]
$Age
,
[string]
$Path
)
Process
{
write-host "Hello World"
If ("Tom","Dick","Jane" -NotContains $Name)
{
Throw "$($Name) is not a valid name! Please use Tom, Dick, Jane"
}
If ($age -lt 21 -OR $age -gt 65)
{
Throw "$($age) is not a between 21-65"
}
IF (-NOT (Test-Path $Path -PathType ‘Container’))
{
Throw "$($Path) is not a valid folder"
}
# All parameters are valid so New-stuff"
write-host "New-Foo"
}
}
Update 3
Update 2
Update 1
If you execute your script directly, it will simply define the Foo function, but never call it.
Place the following after the function definition in your script in order to invoke it with the arguments that the script itself received, using the automatic $args variable via splatting, which allows you to pass arguments via a variable containing an array or hashtable of parameter values, which needs to be referenced with # rather than $:
Foo #args
The alternative would be not to invoke a script file, but a piece of PowerShell code (in PowerShell CLI terms, this means using the -Command parameter rather than -File), which would allow you to use ., the dot-sourcing operator to first load the function definition into the caller's scope, which then allows it to be called:
. "$(System.DefaultWorkingDirectory)/_RodneyConsole1Repo/FunctionExample.ps1"
Foo -Name Rodney -Age 21 -Path ""
I type the following in Powershell, to list the names of all of my azure web applications:
Get-AzureRmWebApp | % { $_.Name }
It outputs:
coolum-exercise-web-app
practice-web-app
AzureSandbox
But then I want to filter this output on the name. I type this:
Get-AzureRmWebApp | ? { $_.Name -like "coolum-exercise-web-app" } | % { $_.Name }
I expect to see only one output. Instead I see
coolum-exercise-web-app
practice-web-app
AzureSandbox
Why didn't the name filter get applied?
If I use the -Name parameter directly on Get-AzureRmWebApp it works:
Get-AzureRmWebApp -Name "coolum-exercise-web-app" | % { %_.Name }
Outputs:
coolum-exercise-web-app
But why does where-object fail to apply the filter as expected?
And here's some really puzzling behavior: if you wrap Get-AzureRmWebApp in brackets, the filter works as you would expect it to.
(Get-AzureRmWebApp) | ? { $_.Name -like "coolum-exercise-web-app" } | % { $_.Name }
Outputs:
coolum-exercise-web-app
Can anyone explain this behavior? Why would enclosing the command in brackets make the filtering work?
Please try with: (notice the brackets)
(Get-AzureRmWebApp) | ? { $_.Name -like 'cool*' }
Looks like the whole where clause is considered as the default named parameter to Get-AzureRmWebApp. That's why you have to seperate the CmdLet from the where clause with brackets.
Actually Get-AzureRmWebApp returns a list, whereas other CmdLet like Get-AzureRmVM returns a single object.
Get-AzureRmWebApp | gm
Get-AzureRmVM | gm
This is a known bug: #1544 Get-AzureRmWebApp - unable to pipe into select-object
The results of Get-AzureRmWebApp is a list. You would expect each item of the list to be sent through the pipe item-by-item. Instead, the entire list gets sent through the pipe once, as a single object.
To demonstrate:
Get-AzureRmWebApp | % { $_.GetType().FullName }
Displays
System.Collections.Generic.List`1[[Microsoft.Azure.Management.WebSites.Models.Site, Microsoft.Azure.Management.Websites, Version=1.0.0.2, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]
While
(Get-AzureRmWebApp) | % { $_.GetType().FullName }
Displays
Microsoft.Azure.Management.WebSites.Models.Site
Microsoft.Azure.Management.WebSites.Models.Site
Microsoft.Azure.Management.WebSites.Models.Site
The bug happens because the underlying C# code invokes WriteObject(sendToPipeline = list), when it should invoke WriteObject(sendToPipeline = list, enumerateCollection = true)
The act of wrapping the call in brackets assigns the returned list to a local temporary object. This local temporary object then behaves like a normal list.
I hope the Azure team fix this, because there will be unintended consequences for hapless automation script writers.
For example, my original call:
Get-AzureRmWebApp | ? { $_.Name -like "coolum-exercise-web-app" } | % { $_.Name }
gets interpreted as "If any of the values has a Name like coolum-exercise-web-app, then display all of the values."
Edit (March 2019)
I have tested this with the Azure Az Powershell Modules and I can see this problem has been fixed.
#Function to get the computerlist: Name,OS,IPv4, IPv6,DiskInfo
function Get-ComputerListnDiskInfo{
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True)] [string[]]$ComputerName
)
BEGIN {
Import-Module ActiveDirectory -Cmdlet Get-ADComputer -ErrorAction SilentlyContinue
}
PROCESS {
try{
$computerinfo = Get-ADComputer -Filter * -Properties OperatingSystem
#Information about Name,Ipv4,IPv6,Device,VolumeName,Free,Busy,Size,Pfree,Pbusy for ALL COMPUTERS container
$AllComputerInfo = #()
foreach ($comp in $computerinfo){
#Testing if computers is ON LINE
$TestCon = Tester $comp.name
$test = $TestCon.BooleanV
if($test) {
#write-output "$Test"
$PhysicalDisks = Get-WMIObject -computername $comp.name -query "SELECT * from win32_logicaldisk where DriveType = 3" | Select Deviceid,VolumeName,FreeSpace,Size
$Target = #()
#Create the Object foreach disk and append in the Target Variable
$GetOPNHealthStatus = Get-PhysicalDisk | select FriendlyName,OperationalStatus,HealthStatus
Write-Output "$PhysicalDisk.count"
#write-output $GetOPNHealthStatus.OperationalStatus
$i=0
foreach ($disk in $physicalDisks){
#Get all Items: size,free,busy,pfree and pbusy disk space info (can add a number at the end to set decimals)
$Size=FormatNSetSizeFreeSpace $disk.Size
$Free=FormatNSetSizeFreeSpace $disk.FreeSpace
$Busy=FormatNSetBusySpace $disk.Size $disk.FreeSpace
$Pfree=PercentFreeBusy $Free $size
$PBusy=PercentFreeBusy $Busy $size
#Create a new Object using all the info
$result =New-Object PSObject -Property #{
Device=$disk.DeviceID
VolumeName=$disk.VolumeName
Size=$Size
Free=$Free
Busy=$Busy
Pfree = $PFree
PBusy = $PBusy
OPStatus = $GetOPNHealthStatus.OperationalStatus[$i]
HStatus = $GetOPNHealthStatus.HealthStatus[$i]
}
$i++
#add this info to the target array
$Target+= $result
}
#Add all info into new object
$allIComnDiskInfo=New-Object PSObject -Property #{
Name = $comp.Name
OS = $comp.OperatingSystem
IPV4 = $TestCon.IPv4
IPV6 = $TestCon.IPv6
disksInfo = $Target
}
#and Fill any just add this info to the $Allcomputer info (just online computer's)
$AllComputerInfo+= $allIComnDiskInfo
}
}
return $AllComputerInfo
}
Catch{
Write-Warning $_.Exception.Message
}
}
}
$test = Get-ComputerListnDiskInfo
running $test
$test = Get-ComputerListnDiskInfo
$test
disksInfo : {#{PBusy=8,148; VolumeName=; Busy=10,306; Pfree=91,853; Free=116,178; Device=C:; Size=126,483; OPStatus=O; HStatus=H}}
Name : DC2012
OS : Windows Server 2012 R2 Standard
IPV4 : 192.168.1.251
IPV6 : fe80::cd63:76bf:3d2b:340f%12
And running
$test | Export-Csv here.csv
I got this:
#TYPE System.String
"Length"
"6"
Why is happening this?
Why I don't get all this info?
And how should I search the info contained in the "diskInfo" variable
I tried to pass this $test variable to another function to format it and It seem not to work:
Thank you in advance for the answers
To start out with, you aren't just outputting a custom object, or an array of custom objects. But that's not the first problem I see. The first problem I see is that you have this big function that has a parameter, and then you do this:
$test = Get-ComputerListnDiskInfo
So you call that function with no arguments, so it has no computer to run it against. Some of the parts of the function will probably default to the local computer, but will they all? I don't know, maybe.
So what does $test actually contain? An array. Of what? Well, the first thing that the function outputs is a string:
Write-Output "$PhysicalDisk.count"
So the first item in your array is a string. Then you build a bunch of custom objects and arrays, and what not, and you Return those. Great, the next item in your $test array is a custom object. But $test is not an array of custom objects, or a single custom object, it is an array with a variety of things within it.
That is why Export-CSV will not work.
Basically the issue is this one:
I have an system.object[] in the output while using CSV.
object or similar output when using export-csv