The function below as well as others that I have in the script to collect a complete inventory of everything we have in AWS, runs without any problems.
However, I am missing all of the IP addresses that are assigned to the instance after the first one when the instance has more than one interface.
How can I make sure to get all the ip addresses of every instance in the function below before writing the details into the excel worksheet?
function Create-EC2InstanceWorksheet {
#Creating EC2 Instances Worksheet
# Add Excel worksheet
$workbook.Worksheets.Add()
# We need to create a sheet for the Instances
$InstancesWorksheet = $workbook.Worksheets.Item(1)
$InstancesWorksheet.Name = 'Instances'
# Headers for the Instance worksheet
$InstancesWorksheet.Cells.Item(1,1) = 'Region'
$InstancesWorksheet.Cells.Item(1,2) = 'Instance Name'
$InstancesWorksheet.Cells.Item(1,3) = 'Image ID'
$InstancesWorksheet.Cells.Item(1,4) = 'Instance ID'
$InstancesWorksheet.Cells.Item(1,5) = 'PEM File'
$InstancesWorksheet.Cells.Item(1,6) = 'Instance Type'
$InstancesWorksheet.Cells.Item(1,7) = 'Private IP'
$InstancesWorksheet.Cells.Item(1,8) = 'Public IP'
$InstancesWorksheet.Cells.Item(1,9) = 'VPC ID'
$InstancesWorksheet.Cells.Item(1,10) = 'Subnet ID'
$InstancesWorksheet.Cells.Item(1,11) = 'State'
$InstancesWorksheet.Cells.Item(1,12) = 'Security Group Id'
$InstancesWorksheet.Cells.Item(1,13) = 'Source/Dest Check'
# Excel Cell Counter
$row_counter = 3
$column_counter = 1
# Get the Ec2 instances for each region
foreach($AWS_Locations_Iterator in $AWS_Locations){
$EC2Instances = Get-EC2Instance -Region $AWS_Locations_Iterator
# Iterating over each instance
foreach($EC2Instances_Iterator.Instances.NetworkInterfaces.PrivateIpAddresses.PrivateIpAddress in $EC2Instances){
foreach($EC2Instances_Iterator.Instances.NetworkInterfaces.Pr ...
+ ~
Missing 'in' after variable in foreach loop.
Remove the code above starting at foreach and used the suggestion provided by #AnthonyNeace. Replaced with the foreach below which does provide the additional ip addresses.
foreach($instance in $EC2Instances.Instances){
foreach($networkInterface in $instance.NetworkInterfaces){
"$($instance.InstanceID): $($networkInterface.PrivateIpAddresses.PrivateIpAddress)";
# Ignore if a region does not have any instances
if($EC2Instances_Iterator.count -eq $null) {
continue
}
# Populating the cells
$InstancesWorksheet.Cells.Item($row_counter,$column_counter++) = $AWS_Locations_Iterator
$InstancesWorksheet.Cells.Item($row_counter,$column_counter++) = $EC2Instances_Iterator.Instances.Tags.value
$InstancesWorksheet.Cells.Item($row_counter,$column_counter++) = $EC2Instances_Iterator.Instances.imageid
$InstancesWorksheet.Cells.Item($row_counter,$column_counter++) = $EC2Instances_Iterator.Instances.Instanceid
$InstancesWorksheet.Cells.Item($row_counter,$column_counter++) = $EC2Instances_Iterator.Instances.keyname.tostring()
$InstancesWorksheet.Cells.Item($row_counter,$column_counter++) = $EC2Instances_Iterator.Instances.Instancetype.Value
$InstancesWorksheet.Cells.Item($row_counter,$column_counter++) = $EC2Instances_Iterator.Instances.PrivateIpAddress
$InstancesWorksheet.Cells.Item($row_counter,$column_counter++) = $EC2Instances_Iterator.Instances.PublicIpAddress
$InstancesWorksheet.Cells.Item($row_counter,$column_counter++) = $EC2Instances_Iterator.Instances.VpcId
$InstancesWorksheet.Cells.Item($row_counter,$column_counter++) = $EC2Instances_Iterator.Instances.SubnetId
$InstancesWorksheet.Cells.Item($row_counter,$column_counter++) = $EC2Instances_Iterator.Instances.state.name.value
$InstancesWorksheet.Cells.Item($row_counter,$column_counter++) = $EC2Instances_Iterator.Instances.securitygroups.GroupId
$InstancesWorksheet.Cells.Item($row_counter,$column_counter++) = $EC2Instances_Iterator.Instances.SourceDestCheck
# Seting the row and column counter for next EC2 instance entry
$row_counter = $row_counter + 1
$column_counter = 1
}
# Iterating to the next region
$row_counter = $row_counter + 1
}
}
The network interface is included with each EC2 Instance in the Get-EC2Instance response, so taking the private ip addresses as an example... you could access the private IP addresses by simply iterating over each private ip address exposed on each network interface. Same for the IPv6 addresses.
Object Model
Amazon.EC2.Model.Instance
System.Collections.Generic.List<Amazon.EC2.Model.InstanceNetworkInterface>
System.Collections.Generic.List<Amazon.EC2.Model.InstancePrivateIpAddress>
PrivateIpAddress
System.Collections.Generic.List<Amazon.EC2.Model.InstanceIpv6Address>
Ipv6Address
IPv4 Example
$EC2Instances_Iterator.Instances.NetworkInterfaces.PrivateIpAddresses.PrivateIpAddress
IPv6 Example
$EC2Instances_Iterator.Instances.NetworkInterfaces.Ipv6Addresses.Ipv6Address
Example: Write addresses string using foreach loop
This example builds the addresses across each network interface into a comma-delimited string within the scope of the instances loop. So a string is created for each instance.
The powershell subexpression operator $() is used to resolve complex properties in the string.
$EC2Instances = Get-EC2Instance
foreach($instance in $EC2Instances.Instances){
$addresses = "";
foreach($networkInterface in $instance.NetworkInterfaces){
$addresses = $addresses, $networkInterface.PrivateIpAddresses.PrivateIpAddress -join ","
}
"$($instance.InstanceID): $($addresses.Trim(','))"
}
Further Reading
AWS Documentation - Get-EC2Instance
Related
I am looking for a solution to get the default audio device via Powershell.
In best case, it could work via embedded C#-code to directly use IMMDeviceEnumerator::GetDefaultAudioEndpoint (see here IMMDeviceEnumertor).
But if it is easier to get this via RegKeys, then this is also OK.
I have seen a couple of code-snippets reading the keys from HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render or \Capture, but I still struggle to identify the DEFAULT device.
It seems, when I do modify the order of devices, then I can simply search for active devices (DeviceState=1) and then sort by the values "Level:0", "Level:1" and "Level:2", but the level-values are not available on a system, where the user has not modied the order manually. What is the sort-criteria in such case?
This is the code-snippet to solve it via RegKeys, but as mentioned - not working for all situations:
$regAudio = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio"
$nameId = "{b3f8fa53-0004-438e-9003-51a46e139bfc},6"
$classId = "{a45c254e-df1c-4efd-8020-67d146a850e0},2"
$driverDetails = "{83da6326-97a6-4088-9453-a1923f573b29},3"
function get-DefaultDevice($type) {
$activeDevices = foreach($key in Get-ChildItem "$regAudio\$type\") {
foreach($item in Get-ItemProperty $key.PsPath) {
if ($item.DeviceState -eq $activeState) {$item}
}
}
$defaultDevice = $activeDevices | Sort-Object -Property "Level:0","Level:1","Level:2" | select -last 1
$details = Get-ItemProperty "$($defaultDevice.PSPath)\Properties"
$name = "$($details.$classId) ($($details.$nameId))"
return #{
name = $name
driver = $details.$driverDetails
}
}
$OsRender = get-DefaultDevice "Render"
$OsCapture = get-DefaultDevice "Capture"
Is there any way to get this info "in a smart way" (without any external DLLs, of course)?
Finally I figured it out and I am happy to share the working code-snippet:
cls
Add-Type #'
[Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDevice {
int a(); int o();
int GetId([MarshalAs(UnmanagedType.LPWStr)] out string id);
}
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDeviceEnumerator {
int f();
int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice endpoint);
}
[ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] class MMDeviceEnumeratorComObject { }
public static string GetDefault (int direction) {
var enumerator = new MMDeviceEnumeratorComObject() as IMMDeviceEnumerator;
IMMDevice dev = null;
Marshal.ThrowExceptionForHR(enumerator.GetDefaultAudioEndpoint(direction, 1, out dev));
string id = null;
Marshal.ThrowExceptionForHR(dev.GetId(out id));
return id;
}
'# -name audio -Namespace system
function getFriendlyName($id) {
$reg = "HKLM:\SYSTEM\CurrentControlSet\Enum\SWD\MMDEVAPI\$id"
return (get-ItemProperty $reg).FriendlyName
}
$id0 = [audio]::GetDefault(0)
$id1 = [audio]::GetDefault(1)
write-host "Default Speaker: $(getFriendlyName $id0)"
write-host "Default Micro : $(getFriendlyName $id1)"
and in case you need a language-neutral international name of each device MMDEVICE-id and (optional) its driver then use this function:
# https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/shared/devpkey.h
# https://github.com/EddieRingle/portaudio/blob/master/src/hostapi/wasapi/mingw-include/mmdeviceapi.h
$regId = "{b3f8fa53-0004-438e-9003-51a46e139bfc},2"
$regName = "{b3f8fa53-0004-438e-9003-51a46e139bfc},6"
$regFormFactor = "{1da5d803-d492-4edd-8c23-e0c0ffee7f0e},0"
# https://learn.microsoft.com/en-us/windows/win32/api/mmdeviceapi/ne-mmdeviceapi-endpointformfactor
$formFactor = #(
"RemoteNetworkDevice",
"Speakers",
"LineLevel",
"Headphones",
"Microphone",
"Headset",
"Handset",
"UnknownDigitalPassthrough",
"SPDIF",
"DigitalAudioDisplayDevice",
"UnknownFormFactor"
)
function getInternationalNameAndDriver($id) {
$guid = $id.Substring(17)
$subKey = #("Render","Capture")[[int]::Parse($id[5])]
$reg = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\$subKey\$guid\Properties"
$details = get-ItemProperty $reg -ea 0
if ($details) {
$id = $details.$regId.subString(4)
$name = $details.$regName
$form = $formFactor[$details.$regFormFactor]
$hardware = get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Enum\$id"
$regDrv = $hardware.Driver
$driver = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Class\$regDrv"
$drvName = $driver.DriverDesc
$drvVersion = $driver.DriverVersion
}
return "$form ($name), driver: $drvName $drvVersion"
}
this gives you an output like this:
Default Speaker: Speakers (Realtek Audio), driver: Realtek Audio 6.0.1.6127
Default Micro : Microphone (Logitech BRIO), driver: USB Audio Device 10.0.22000.653
So I need help tidying up a script that I have. The purpose of this script is to make 18 different sql files based on the data below 18 different column headers. What my script does now is make 1 sql file based on which column I choose to input via "Read-Host". This is my current script
function get-header
{Read-Host "Type the Column header betwen B-Z for which sql files needs to be created"
}
function get-column
{
Read-Host "Type the Column number"
}
do
{
$val = get-header
}
while(!($val))
do
{$col = get-column
}
while(!($col))
switch ($val)
{
"B"{$column = "1"}
"C"{$column = "2"}
"D"{$column = "3"}
"E"{$column = "4"}
"F"{$column = "5"}
"G"{$column = "6"}
"H"{$column = "7"}
"I"{$column = "8"}
"J"{$column = "9"}
"K"{$column = "10"}
"L"{$column = "11"}
"M"{$column = "12"}
"N"{$column = "13"}
"O"{$column = "14"}
"P"{$column = "15"}
"Q"{$column = "16"}
"R"{$column = "17"}
"S"{$column = "18"}
"T"{$column = "19"}
"U"{$column = "20"}
"V"{$column = "21"}
"W"{$column = "22"}
"X"{$column = "23"}
"Y"{$column = "24"}
"Z"{$column = "25"}
default { $column = 'Unknown' }
}
if ($column -eq 'Unknown')
{
Write-Warning "Not a valid input"
return
}
$csv = Import-Csv "Indices Updates - September 2018.csv" -Header 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26
$date = (Get-Date -Format "MM/dd/yyyy").Replace("-","/")
$sql = #("INSERT INTO benchmark_values(created_at,benchmark_id,date,amount,created_by_username)
foreach($data in $csv)
{
$secondcolumn = [int]$column + 1
$sql += "('$date',$col,'$($data.1)',$($data.$secondcolumn),'BPylla'),"
}
$sql | Out-File "sqldata.sql"
Now I want to get rid of read-host entirely because I dont want to input any values. I also will give an example of what the csv file looks like and what the sql file should look like.
So the goal is to produce different sql files from each column of information using the the sql format posted. I already have the template for that in my script now, I just need the script to create all the sql files based on headers and still input the data below the headers in the new sql files. Any help would be greatly appreciated! Thanks!
Is there a way to pull multiple SNMP values and update them as new values in PowerShell?
I have two different OIDs that I'm pulling from my UPS and I want those integer values saved to a database table.
This is my code
$UPS_Temp_oid = '1.3.6.1.4.1.318.1.1.10.2.3.2.1.4.1'
$Battery_Load_oid = '.1.3.6.1.4.1.318.1.1.1.4.2.3.0'
$sql = "SELECT temp, batteryload, upsid, ups_ip FROM ups WHERE ups_ip IS NOT NULL"
$cmd = New-Object System.Data.SqlClient.SqlCommand($sql, $conn)
$rows = $cmd.ExecuteReader()
while ($rows.Read()) {
$ups_id = $rows["upsid"]
$ups_ip = $rows["ups_ip"].trim()
$ups_temp = $rows["temp"]
$battery_load = $rows["batteryload"]
Write-Output $ups_id, $ups_ip, $ups_temp, $battery_load
# Ping UPS
$ping = New-Object System.Net.NetworkInformation.Ping
$ping_reply = $ping.Send($ups_mgmt_ip) | select status
# If success go call func SNMP
if ($ping_reply.status -eq "Success") {
try {
$frm_snmp = Invoke-SNMPget $ups_ip $UPS_Temp_oid "community"
} catch {
Write-Host "$ups_mgmt_ip SNMP Get error: $_"
return null
}
would I have to create another try..catch for the battery_load_oid or would I just simply do something like that?
Invoke-SNMPget ups_ip $ups_temp_oid, $nattery_load_oid "community"
I have a question about how to add users to AD using powershell, ive written a small script but i always get an error when i try to create a user.
$connection= "LDAP://ou=Users, dc="domain", dc="com"
$OU = [adsi] $Connection
$User = $OU.Create("user", "Test Person")
$User.Put("Firstname", "Test")
$User.Put("Surname", Person)
$User.Put("Email", "email#e.com")
$User.SetInfo()
I think my connection string is wrong, but i tried different ways already and still no success. This im trying locally. Need to get it working but then normally my AD is on different server, how to do it then?
Thanks in advance.
Give this a try:
$container = [ADSI] "LDAP://dc.sopragroup.lan/cn=Users,dc=sopragroup,dc=lan"
$UserName = "user"
$User = $container.Create("User", "cn=" + $UserName)
$User.Put("sAMAccountName", $UserName)
$User.Put("givenName", "Test")
$User.Put("sn", "Person")
$User.Put("mail", "email#e.com")
$User.SetInfo()
$User.psbase.InvokeSet('AccountDisabled', $false)
$User.SetInfo()
$User.SetPassword("P#55w0rd")
Here is another example (#Andy Arismendi was first) with some other details:
If you want to give a user and a password (log onto the server with a different user than the current one), you can use the DirectoryEntry constructor
An error that is commonly done is that when you create an object in a directory, the name that represent this object in the directory tree is built with the construction : attribute=value. In Active-Directory you can't choose the the attribute it's imposed by the schema. For a user or an inetOrgPerson it's CN for an organizationalUnit it's OU. In your case the name of the object is CN=Test Person.
You'll find here under the creation of an OU and a user.
Clear-Host
$dn = New-Object System.DirectoryServices.DirectoryEntry ("LDAP://192.168.234.200:389/dc=dom,dc=fr","administrateur#dom.fr","admin")
# Create an OU
$Monou = $dn.create("OrganizationalUnit", "ou=Monou")
#$Monou.Description = "Une description"
$Monou.put("Description", "Une description")
$Res = $Monou.Setinfo()
# Create a user
$objUtilisateur = $Monou.create("inetOrgPerson", "cn=Marc Assin")
$objUtilisateur.setinfo()
$objUtilisateur.samaccountname = "Massin"
$objUtilisateur.givenName = "Marc"
$objUtilisateur.sn = "Assin"
#$objUtilisateur.displayName = $objUtilisateur.givenName + " " + $objUtilisateur.sn
$objUtilisateur.userPrincipalName = "Massin#dom.fr"
# Pu the state of the account#$objUtilisateur.SetPassword("test.2010")
$objUtilisateur.pwdLastSet = 0
$objUtilisateur.userAccountControl = 544
# Write the datas of the user
$objUtilisateur.SetInfo()
I am trying to use capabilities of DbConnectionStringBuilder for parsing connection-string-like user input. This works just fine in C#:
using System;
using System.Data.Common;
class Program
{
static void Main(string[] args)
{
var sb = new DbConnectionStringBuilder();
sb.ConnectionString = "server = 'smtp.gmail.com'; port = 587; user = you#gmail.com";
Console.WriteLine(sb["server"]);
Console.WriteLine(sb["port"]);
Console.WriteLine(sb["user"]);
}
}
Output:
smtp.gmail.com
587
you#gmail.com
But it does not work in PowerShell. Namely, this literally translated code in PowerShell
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
$sb.ConnectionString = "server = 'smtp.gmail.com'; port = 587; user = you#gmail.com"
$sb["server"]
$sb["port"]
$sb["user"]
produces no output at all.
Any ideas why? How to make DbConnectionStringBuilder to work as a parser in PowerShell?
System.Data.Common.DbConnectionStringBuilder implements IDictionary. Powershell has a shorthand for dictionaries using . that allows retrieval and assignment of key/value pairs as if the key was a property:
$dict = #{}
$dict["key1"] = 'value1'
$dict.key2 = 'value2'
You can see that it is storing the entire connection string as a key/value pair instead of on the ConnectionString property this way:
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
$sb.ConnectionString = "server = 'smtp.gmail.com'; port = 587; user = you#gmail.com"
$sb #formatted table of key/value pairs
The easiest way to get around this is to call the set_/get_ methods generated for properties:
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
$sb.set_ConnectionString("server = 'smtp.gmail.com'; port = 587; user = you#gmail.com")
$sb["server"]
$sb["port"]
$sb["user"]
It is probably a bug (a gotcha anyway) and I will submit it soon. It looks like PowerShell does not call setters on properties of classes that implement IDictionary (as DbConnectionStringBuilder does, and it is the setter of ConnectionString that parses the string).
Here are two demos (the original and workaround):
# test 1 - does not work, presumably PowerShell invokes $sb["ConnectionString"] = .. instead of the setter
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
$sb.ConnectionString = "server = 'smtp.gmail.com'; port = 587; user = you#gmail.com"
# the original string
$sb.ConnectionString
# nothing at all
$sb["server"]
$sb["port"]
$sb["user"]
# test 2 - works fine, we make PowerShell to invoke the ConnectionString property setter in this way
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
$sb.PSObject.Properties['ConnectionString'].Value = "server = 'smtp.gmail.com'; port = 587; user = you#gmail.com"
# parsed and re-formatted string
$sb.ConnectionString
# parsed data
$sb["server"]
$sb["port"]
$sb["user"]
Output:
server = 'smtp.gmail.com'; port = 587; user = you#gmail.com
server=smtp.gmail.com;port=587;user=you#gmail.com
smtp.gmail.com
587
you#gmail.com
As far as the workaround is found we get for free a pretty powerful parser for connection-string-like data. Here is the demo that shows parsing of quite convoluted input:
# get the parser
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
# input the string to parse using this workaround way
$sb.PSObject.Properties['ConnectionString'].Value = #'
Apostrophes = "Some 'value'";
Quotations = 'Some "value"';
Semicolons = '1; 2; 3';
Multiline = Line1
Line2
Line3;
Name with spaces = Some value;
Name with == sign = Some value;
'#
# get the parsed results
# the string: not the original but parsed and re-formatted
$sb.ConnectionString
# the data: parsed key/values
$sb | Format-Table -AutoSize -Wrap
Output:
apostrophes="Some 'value'";quotations='Some "value"';semicolons="1; 2; 3";multiline="Line1
Line2
Line3";name with spaces="Some value";name with == sign="Some value"
Key Value
--- -----
apostrophes Some 'value'
quotations Some "value"
semicolons 1; 2; 3
multiline Line1
Line2
Line3
name with spaces Some value
name with = sign Some value
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
$sb.Add("server","smtp.gmail.com")
$sb.Add("port",587)
$sb.Add("user","you#gmail.com")
$sb["server"]
$sb["port"]
$sb["user"]