Cloud - AWS only for now - API integration for IPAM

What follows is a simple approach to using AWS API calls to get the full list of: VPCs and Subnets, and update them to Supernets and Subnets under a folder in IPAM.

Why do this?  For more efficient management of IP addresses in cloud.  No scanning subnets required.  Accounts can remain entirely seperate from your corporate network - and still be managed.

Tag and other AWS information is populated into custom fields, and used to set subnet parent objects in the tree.  API calls are used for Solarwinds where available, and direct SQL commands are used where they are not.

This work has not been completed.  TODO:

  1. code to delete subnets and VPCs that no longer exist
  2. Subnets - the code will be very similar to the VPCs function.  I have copied the VPC code to get started
  3. IP addresses
  4. change code to query to get the list of accounts accessible by IAM roles.   Currently an array is set with the ARNs for each IAM role, to get the code to run.
  5. set hostname, etc, as with the objects all set to manual, this also needs to be done manually

 frak_0-1612316204159.png

Requirements

  1. Solarwinds powershell lib
  2. AWSPowershell.NetCode lib
  3. MS SQL Server powershell lib
  4. AWS account 
  5. IAM roles for each account with access to read network properties, and list of ARNs for these.
  6.  A folder in IPAM to store all this in.
  7. The following custom properties for IPAM groups:
    1. aws_cloudformation_logical_id
    2. aws_cloudformation_stack_id
    3. aws_cloudformation_stack_name

Code

$SW_Server = ""
$SQL_Server = ""
$AWS_GroupName = 'Amazon AWS'
 
 
 
#FUNCTIONS
 
#IPAM Update Functions
 
#insert/update IPAM VPC folder based on existance within Solarwinds
Function Upsert-AWS-VPC{ Param ( [array]$vpcs, [SolarWinds.InformationService.Contract2.InfoServiceProxy]$swis, [string]$AWS_RootID )
Process{
    foreach ($vpc in $vpcs) {
        $name = $vpc.Name
        $ip = $vpc.CidrBlock.Split('/')[0]
        $cidr = $vpc.CidrBlock.Split('/')[1]
 
        $AWS_GroupID = Get-SwisData -SwisConnection $swis `
            -Query "SELECT TOP 1 GroupId FROM IPAM.GroupNode where address = '$ip' and cidr = '$cidr' "
 
        if ($AWS_GroupID -eq $null) {
            "create $ip - $cdr "
            Invoke-SwisVerb $swis IPAM.SubnetManagement CreateSubnet @($ip, $cidr )   
        }
       
        #update properties
        # subnetID and URI are both IDs that refer to the same object
        $URI = Get-SwisData -SwisConnection $swis `
            -Query "SELECT TOP 1 URI FROM  IPAM.GroupNode where address = '$ip' and cidr = '$cidr' "
        $SubnetID = Get-SwisData -SwisConnection $swis `
            -Query "SELECT TOP 1 GroupID FROM  IPAM.GroupNode where address = '$ip' and cidr = '$cidr' "
 
        #disable auto scan
        # We do NOT want solarwinds ICMP scanning and changing the staus of things.  Also means we
        # need to manage everything including (I think?) DNS and host data. 
        Invoke-SwisVerb $swis IPAM.SubnetManagement ChangeDisableAutoScanning  @( $SubnetID, "false")
 
        #set custom properties
        #not supported yet by API
        #Set-SwisObject $swis -Uri $URI -Properties @{Object_ID=$ff}
        #Set-SwisObject $swis -Uri $URI -Properties @{aws_cloudformation_logical_id=$ff}
        #Set-SwisObject $swis -Uri $URI -Properties @{aws_cloudformation_stack_id=$ff}
        #Set-SwisObject $swis -Uri $URI -Properties @{aws_cloudformation_stack_name=$ff}
        #Set-SwisObject $swis -Uri $URI -Properties @{OwnerId=$ff}
       
        $sql = " 
update [SolarWindsOrion].[dbo].[IPAM_GroupAttrData]
SET [Object_ID] = '" + $vpc.vpcid +  "'
       ,[aws_cloudformation_logical_id] = '"+ $vpc.'aws:cloudformation:logical-id' +"'
      ,[aws_cloudformation_stack_id] = '"+ $vpc.'aws:cloudformation:stack-id' +"'
      ,[aws_cloudformation_stack_name] = '"+ $vpc.'aws:cloudformation:stack-name' +"'
      ,[OwnerId] = '"+ $vpc.OwnerId +"'
where groupid = $SubnetID"
 
        $rs = Invoke-Sqlcmd -ServerInstance $SQL_Server -Database SolarwindsOrion -Query $sql
 
        #set basic properties
        #note groupType=4 means supernet
        $sql = "
update dbo.ipam_group
SET parentid = $AWS_RootID
       ,FriendlyName = '" + $vpc.Name + "' + Address + ' /' + cast(CIDR as varchar(10))
       ,GroupType = 4
where groupid = $SubnetID"
 
        $rs = Invoke-Sqlcmd -ServerInstance $SQL_Server -Database SolarwindsOrion -Query $sql
 
 
    }
 
}
}
 
 
#insert/update IPAM folder based
#this still needs writing.  Currently just a copy of the code for the above function as it will be similar.
#we do not need to change our subnet to a supernet.
Function Upsert-AWS-Subnet{ Param ( [array]$vpcs, [SolarWinds.InformationService.Contract2.InfoServiceProxy]$swis, [string]$VPC_ID )
Process{
    foreach ($vpc in $vpcs) {
        $name = $vpc.Name
        $ip = $vpc.CidrBlock.Split('/')[0]
        $cidr = $vpc.CidrBlock.Split('/')[1]
 
        $AWS_GroupID = Get-SwisData -SwisConnection $swis `
            -Query "SELECT TOP 1 GroupId FROM IPAM.GroupNode where address = '$ip' and cidr = '$cidr' "
 
        if ($AWS_GroupID -eq $null) {
            "create $ip - $cdr "
            Invoke-SwisVerb $swis IPAM.SubnetManagement CreateSubnet @($ip, $cidr )   
        }
       
        #update properties
        # subnetID and URI are both IDs that refer to the same object
        $URI = Get-SwisData -SwisConnection $swis `
            -Query "SELECT TOP 1 URI FROM  IPAM.GroupNode where address = '$ip' and cidr = '$cidr' "
        $SubnetID = Get-SwisData -SwisConnection $swis `
            -Query "SELECT TOP 1 GroupID FROM  IPAM.GroupNode where address = '$ip' and cidr = '$cidr' "
 
        #disable auto scan
        # We do NOT want solarwinds ICMP scanning and changing the staus of things.  Also means we
        # need to manage everything including (I think?) DNS and host data. 
        Invoke-SwisVerb $swis IPAM.SubnetManagement ChangeDisableAutoScanning  @( $SubnetID, "false")
 
        #set custom properties
        #not supported yet by API
        #Set-SwisObject $swis -Uri $URI -Properties @{Object_ID=$ff}
        #Set-SwisObject $swis -Uri $URI -Properties @{aws_cloudformation_logical_id=$ff}
        #Set-SwisObject $swis -Uri $URI -Properties @{aws_cloudformation_stack_id=$ff}
        #Set-SwisObject $swis -Uri $URI -Properties @{aws_cloudformation_stack_name=$ff}
        #Set-SwisObject $swis -Uri $URI -Properties @{OwnerId=$ff}
       
        $sql = " 
update [SolarWindsOrion].[dbo].[IPAM_GroupAttrData]
SET [Object_ID] = '" + $vpc.vpcid +  "'
       ,[aws_cloudformation_logical_id] = '"+ $vpc.'aws:cloudformation:logical-id' +"'
      ,[aws_cloudformation_stack_id] = '"+ $vpc.'aws:cloudformation:stack-id' +"'
      ,[aws_cloudformation_stack_name] = '"+ $vpc.'aws:cloudformation:stack-name' +"'
      ,[OwnerId] = '"+ $vpc.OwnerId +"'
where groupid = $SubnetID"
 
        $rs = Invoke-Sqlcmd -ServerInstance $SQL_Server -Database SolarwindsOrion -Query $sql
 
        #set basic properties
        #note groupType=4 means supernet
        $sql = "
update dbo.ipam_group
SET parentid = $AWS_RootID
       ,FriendlyName = '" + $vpc.Name + "' + Address + ' /' + cast(CIDR as varchar(10))
       ,GroupType = 4
where groupid = $SubnetID"
 
        $rs = Invoke-Sqlcmd -ServerInstance $SQL_Server -Database SolarwindsOrion -Query $sql
 
 
    }
 
}
}
 
 
 
 
 
 
#######################################
'start...'
#initialise Solarwinds API
# load the snappin if it's not already loaded (step 1) 
if (!(Get-PSSnapin | Where-Object { $_.Name -eq "SwisSnapin" })) { 
    Add-PSSnapin "SwisSnapin" 
} 
$swis = Connect-Swis -Hostname $SW_Server -Trusted
 
#check that IPAM group folder exists
$AWS_RootID = Get-SwisData `
    -SwisConnection $swis `
    -Query "SELECT TOP 1 GroupId FROM IPAM.GroupNode where FriendlyName = '$AWS_GroupName'"
if ( $AWS_GroupID -eq $null ){
    'exit bad!'
    exit
}
 
#initialise AWS API – and proxy access (if you use it)
import-module AWSPowerShell.NetCore
set-awsproxy -hostname “xxx” -port 9090
 
#this is the list of IAM roles/accounts.  There must be a better way to get this info – instead of this manualy compiled list.
$arnList = 'arn:aws:iam::999:role/role--monitoring', 'arn:aws:iam::993:role/role-monitoring', `
            'arn:aws:iam::991:role/role--monitoring', 'arn:aws:iam::992:role/role-monitoring'
 
#account access key
set-awscredential -accesskey 'xxxx'  -secretkey yyyy' -StoreAs solar
Initialize-AWSDefaults -ProfileName solar -Region ap-southeast-2
 
#build ARN list
$creds = [System.Collections.ArrayList]@()
foreach( $arn in $arnList ) {
    $arn
    $cr = $null
    $cr = (Use-STSRole -RoleArn $arn -RoleSessionName "solar").Credentials
    if ( $cr ) {   
        $cr | add-member -NotePropertyName 'arn' -NotePropertyValue $arn    
        $cr
        $creds.Add( $cr )
    }
}
 
 
#loop through the creds array.   Hit each account in turn
foreach( $cred in $creds ){
    #get all subnets in account.  Will be children of the supernets
    $subn = Get-ec2subnet -Credential $Cred     
    $subnets = @($subn | select-object  AvailabilityZone, CidrBlock, State, SubnetId, VpcId, @{Name='Name'; Expression = {  ($_.Tags | Where-Object {$_.Key -eq 'Name'}).Value   }} `
        ,@{Name='aws:cloudformation:logical-id'; Expression = {  ($_.Tags | Where-Object {$_.Key -eq 'aws:cloudformation:logical-id'}).Value   }} `
        ,@{Name='aws:cloudformation:stack-id'; Expression = {  ($_.Tags | Where-Object {$_.Key -eq 'aws:cloudformation:stack-id'}).Value   }} `
        ,@{Name='aws:cloudformation:stack-name'; Expression = {  ($_.Tags | Where-Object {$_.Key -eq 'aws:cloudformation:stack-name'}).Value   }}  )
   
    #get all VPCs in accounts.  Will be supernets, and children of the main AWS folder (see variable at top of script)
    $vpc = Get-EC2Vpc -Credential $Cred
    $vpcs = @($vpc | select-object CidrBlock, OwnerId, VpcId, @{Name='Name'; Expression = {  ($_.Tags | Where-Object {$_.Key -eq 'Name'}).Value   }} `
    ,@{Name='aws:cloudformation:logical-id'; Expression = {  ($_.Tags | Where-Object {$_.Key -eq 'aws:cloudformation:logical-id'}).Value   }} `
    ,@{Name='aws:cloudformation:stack-id'; Expression = {  ($_.Tags | Where-Object {$_.Key -eq 'aws:cloudformation:stack-id'}).Value   }} `
    ,@{Name='aws:cloudformation:stack-name'; Expression = {  ($_.Tags | Where-Object {$_.Key -eq 'aws:cloudformation:stack-name'}).Value   }}  )
 
    #TODO.  Call Get-EC2Subnet for all IPs in use
 
 
    Upsert-AWS-VPC -vpcs $vpcs -swis $swis -AWS_RootID $AWS_RootID
   
    
}
 
 
 
 
Remove-AWSCredentialProfile -ProfileName solar