The following code works, but I'm interested in knowing if there would have been a more efficient way of writing the script, may be by using loops, or it this is the correct way to write such a script? The problem I had when trying to use loop statements was that I couldn't work out how to put the $Dialog "OK" into a loop where it can then loop back to itself if the IP Address still wasn't valid.
The idea of the script is to get the (first three octets of an) IP address, and see if it's valid (i.e not 0.0.0.0 or 169.254.*) before storing it as a variable, and if it isn't valid to throw a dialog box to give to administrator the opportunity to correct it, and then check again, and so on.
function Check-IP
{
$IPSiteAddress = Get-IPAddress
if ($IPSiteAddress -like "0.*" -or $IPSiteAddress -like "169.254.*") {DialogBox-IP}
}
function Get-IPAddress
{
(Get-WmiObject Win32_NetworkAdapterConfiguration |
Where { $_.IPAddress } |
Select -Expand IPAddress).split('.')[0..2] -join '.'
}
function DialogBox-IP
{
$IPDialog = [System.Windows.Forms.MessageBox]::show( "This computer doesn't have a valid IP Address.
Please correct the IP Address and click OK, or click Cancel to exit.","No Network Connection",1)
if ($IPDialog -eq "OK") {Check-IP} else {exit}
}
Check-IP
$IPSiteAddress = Get-IPAddress
If anyone has a nicer solution or, any thoughts, I'll love to hear them
function Check-IP
{
param ($IPSiteAddress)
return !($IPSiteAddress -like "0.*" -or $IPSiteAddress -like "169.254.*")
}
function Get-IPAddress
{
(Get-WmiObject Win32_NetworkAdapterConfiguration | Where { $_.IPAddress } | Select -Expand IPAddress).split('.')[0..2] -join '.'
}
while (!(Check-IP Get-IPAddress))
{
DialogBox-IP
}
Related
I have a Powershell script returning data from an API which works fine as long as I only attempt to return one $device.realm, but I need multiple realms. I'm a newb to PS.
Any help here is really appreciated
Here is my code
$Output = forEach ($device in $devices) {
if ($device.realmName -eq 'Archive') {
[PSCustomObject]#{
HostName = $device.name
IPAddress = $device.primaryInterfaceAddress
Realm = $device.realmName
SerialNumbers = (($device.dynamicFields | where { $_.name -EQ "serial number" } | Select-Object -ExpandProperty values) -join "," | out-string).TrimEnd()
}| Select-Object Hostname,IPAddress,Realm,SerialNumbers | Export-csv C:\temp\Archive.csv -notype -Append
}
I need to return multiple $device.realms as in
if ($device.realmName -eq 'Archive' -and 'Default' -and 'Farms')
Once I add the additional -and's every realm is returned instead of just the one's I need to return.
I believe the issue at hand here is that the statement within the If block that you're querying as ($device.realmName -eq 'Archive' -and 'Default' -and 'Farms')
is not, when evaluated logically "Evaluate true if the device realmname is Archive, Default, or Farms." It is evaluating whether device.realmname is archive, and then just interpreting the two -ands in your example as true, as they are not querying a comparison, but just the presence of a non-null string. Not sure what is leading it to return everything, I'd have to see some more direct examples to be sure, but in my experience that is most common when you include an -or in a comparison pointing to a nonnull string, which will make the entire statement true.
What I would suggest is as follows: Use the regex operators built in to powershell for cases like this. You could use
if($device.realmname -eq 'Archive' -or $Device.realmname -eq 'farm' -or $device.realmname -eq 'Default')
which would, I believe, return what you are looking for, but I find it a bit complex. More complicated queries on a single property, I find, are easiest to do via -match, through something invoking the -match operator, which allows you to build a regex query statement that can include Or's or And's with a bit simpler of a synatax, like so:
if($Device.realmName -match 'Archive|Farm|Default')
I am attempting to query AD to get the phone number for a user then format it to a standard format (###-####). I'm using a Switch statement because I've seen the number in a handful of different formats. With the way I have my code set up though I am getting an error: "The term Switch is not recognized as a name of a cmdlet, function, script file..."
Here's the code:
$ADInfo = Get-ADUser $User.Email.split("#")[0] -Properties * -Server $DC
$User.'Phone Number' = $ADInfo.telephoneNumber | Switch -regex ($_) {
'^\d{5}$'
{
"{0:38#-####}" -f $_
break
}
'^\d{7}$'
{
"{0:###-####}" -f $_
break
}
default
{
break
}
}
Am I misunderstanding how the pipeline works? I suppose I could just save this info to a temp variable and then enter a Switch statement but this seemed like an effective way to use the pipeline.
Anyhow, any help is appreciated! Thanks!
Am I misunderstanding how the pipeline works?
Yes.
The pipeline can only pipe stuff to commands - and switch is not a command, it's a language keyword.
You can wrap your switch statement in a ForEach-Object block and pipe the input to that:
$User.'Phone Number' = $ADInfo.telephoneNumber | ForEach-Object {
Switch -regex ($_) {
'^\d{5}$'
{
"{0:38#-####}" -f $_
}
'^\d{7}$'
{
"{0:###-####}" -f $_
}
}
}
As Ansgar points out, the break statements here are redundant (and not required), since your test cases are mutually exclusive
As suggested by Mathias R. Jessen, here's an example of how to use Switch without having to iterate through things with a ForEach loop, or piping anything to it.
Switch will iterate an array on its own, so there's no need to nest it in a loop, or even pipe anything in this case. It can be done as such:
$ADInfo = Get-ADUser $User.Email.split("#")[0] -Properties * -Server $DC
$User.'Phone Number' = Switch -regex ($ADInfo.telephoneNumber) {
'^\d{5}$'
{
"{0:38#-####}" -f $_
}
'^\d{7}$'
{
"{0:###-####}" -f $_
}
}
Aside from that I would recommend using Continue instead of Break. Further example of when continue would be used within the Switch scriptblock:
$TestData = #('John.Doe#Company.Com','JDoe','(555)867-5309','Taco Tuesday')
Switch -regex ($TestData){
'#' {"$_ is an email address.";continue}
'^[+()\-\d\s]+$' {"$_ is a phone number.";continue}
default {"$_ is unknown."}
}
Here we have something being done in the default block, so we want to include a continue statement in the previous cases so that if a match is found it moves to the next item in the array, and does not execute the default case. The output of that would be:
John.Doe#Company.com is an email address.
JDoe is unknown.
(555)867-5309 is a phone number.
Taco Tuesday is unknown.
A trivial question, but hopefully really obvious for those who know.
Search constructor:
$Search = New-Object System.DirectoryServices.DirectorySearcher
(([adsi]"LDAP://ou=Domain Users,dc=example,dc=pri"),'(objectCategory=person)',
('name','employeeID'))
I want to exclude results where the employeeID attribute does not exist.
This works:
$users = $Search.FindAll()
ForEach ($u in $users) {
If ($u.properties.employeeid) {
Write-Host $($u.properties.name)
}
}
The following does not work - no output. However, when the IF statement is omitted, results are output.
ForEach ($user in $($Search.FindAll())) {
If ($user.properties.employeeID) {
Write-Host $($user.properties.name)
}
}
Is it a syntax issue in the second example, or do I just need to temporarily store results in an object before running conditional statements on them?
(To circumvent any discussion on why not use the ActiveDirectory module and Get-ADUser, it's for a user that cannot have the module installed on their workstation, nor be granted perms to invoke it via a PSSession on a host where it is installed.)
Update: found a slightly nicer way of doing the where clause:
$searcher.FindAll() | where { ($_.properties['employeeid'][0]) }
Just remove if statement and filter search results:
$users = $Search.FindAll() | Where-Object {-not [string]::IsNullOrEmpty($_.properties.employeeID)}
So here's what I'm attempting to do:
I manually input a name, and then I want to get a list of users who work under the person whose name I input (extensionattribute9 is who the user works under). However, for each person that works under that person, I also want to run the process for them, and see if anyone works under them as well. I want this process to continue until no one works under the current user.
I've managed do to this up to 3 times without a while loop, but as I don't know how deep I would have to go to get everyone, I feel using a while loop would be better overall, especially in terms of code length.
Here is the code I currently have:
$users = Get-ADUser -Filter * -Properties extensionattribute9,Displayname,mail,title
$users | ForEach-Object {
if ($_.extensionattribute9 -like '*Lynn, *')
{
$_ | select Displayname,userprincipalname,title,extensionattribute9
$name = $_.Displayname
while ($_.extensionattribute9 -ne $null){ $users | ForEach-Object {
if ($_.extensionattribute9 -eq $name)
{
$_ | select Displayname,userprincipalname,title,extensionattribute9
$name=$_.Displayname
}
}
}
}
}
When I run the code I get a user (User A) under 'Lynn', and then a user under User A. After that, nothing. The program still continues to run, but nothing gets returned. I'm guessing it's stuck in an infinite cycle, but I don't know where better to put the while loop. Help?
It sounds like you are trying to do a recursive search with nested while/for-each loops which can end badly. You can try something like this:
Function Get-Manager {
param([Object]$User)
$ManagerName = $User.extensionattribute9
# Recursion base case
if ($ManagerName -eq $null){
return
}
# Do what you want with the user here
$User | select Displayname, userprincipalname, title, extensionattribute9
# Recursive call to find manager's manager
$Manager = Get-ADUser -Filter "Name -like $ManagerName"
Get-Manager $Manager
}
# Loop through all ADusers as you did before
$Users = Get-ADUser -Filter * -Properties extensionattribute9,Displayname,mail,title
Foreach ($User in $Users) {
Get-Manager $User
}
Please note I don't have experience using Powershell with Active Directory so the syntax may be incorrect, but shouldn't be hard to fix. Hope this helps!
I'm not familiar with Powershell, but one possible reason you're having trouble is that $_ is being used to mean two different things, depending on whether you use it inside the while loop or not. Is Powershell really smart enough to know what you mean?
More important: the code
$_ | select Displayname,userprincipalname,title,extensionattribute9
$name = $_.Displayname
appears in two places close together. This is a definite code smell. It should appear once and only once.
When you're traversing a hierarchy and you don't know how deep it will go, you must use a recursive algorithm (a function that calls itself). Here it is in pseudocode:
function myFunc ($node) {
//report the name of this node
echo "node $node found." ;
// check to see if this node has any child nodes
array $children = getChildNodes ($node) ;
if (count($children) == 0) {
//no child nodes, get out of here
return ;
}
//repeat the process for each child
foreach($children as $child) {
myFunc($child) ;
}
}
I have the below bit of code, it is designed to simply ask for a Windows ProcessName, and then return a list of all instances of that process that are running.
$processName = Read-Host 'Please Enter the Process Name, as shown in Task Manager (not inc *32)'
if (!$processName)
{
"No Process Given"
}
else
{
"You Entered "+$processName
$filter = "name like '%"+$processName+"'"
$result = Get-WmiObject win32_process -Filter $filter | select CommandLine
$counter=1
foreach($process in $result )
{
write-host "$counter) $process"
$counter++
}
}
It works fine up until the point where it outputs the list.
If I do
echo $process
then I get what I am after e.g.
"C:\folder\AppName.exe"
"C:\folder\AppName.exe instance1"
If however I try to concatenate the $counter in front of it I get:
1) #{CommandLine="C:\folder\AppName.exe" }
2) #{CommandLine="C:\folder\AppName.exe instance1" }
I've tried write-host, write-output, echo, various combinations of "", +, but I can't get rid of the #{CommandLine= xxx } when I try to combine it with another variable
Is there a way to get what I am after? e.g.:
1) "C:\folder\AppName.exe"
2) "C:\folder\AppName.exe instance1"
try write-host "$counter) $($process.commandline)"
OR modify your selection :
$result = Get-WmiObject win32_process -Filter $filter | select -expandproperty CommandLine
explanation : without expandproperty you get a psobject with expandproperty you have a string