WhoRMi.psm1

<#
    .SYNOPSIS
 
    When you run this on an Azure VM it returns the metadata associated with the virtual machine using the local VM Metadata service
 
    .DESCRIPTION
 
    Get-VMMetadata connects to the local metadata service which runs on the same physical host as the virtual machines are hosted on. The service returns some really useful information such as:
 
        location
        name
        resourceGroupName
        subscriptionId
        vmId
        vmSize
        tags
        version
     
        offer
        osType
        publisher
        sku
        apiVersion
        placementGroupID
        platformFaultDomain
 
        ipv4 address details
        mac address
 
        RawResponse - in the extremely likely event that microsoft update the service before I do, or someone else does you can get to the raw content using RawResponse | ConvertFrom-Json
            you will also need to pass in the api version parameter to get a later version
 
#>

Function Get-VMMetadata {
    [CmdletBinding()]    
    param(
        $apiVersion = '2017-08-01',
        $hostName = '169.254.169.254',
        $uri = 'metadata/instance'
    )   

    $url = "http://$hostName/$($uri)?api-version=$apiVersion"
    Write-Verbose $url
    
    class NetworkInterface {
        [string]$ip4PrivateAddress
        [String]$ip4PublicAddress
        [String]$ip4SubnetAddress        
        [String]$ip4SubnetPrefix

        [String]$macAddress

    }

    class InstanceMetadata {
    
        [String]$location
        [String]$name
        [String]$resourceGroupName
        [String]$subscriptionId
        [String]$vmId
        [String]$vmSize
        [String]$tags
        [String]$version
    
        [String]$offer
        [String]$osType
        [String]$publisher
        [String]$sku
        [String]$apiVersion


        [String]$placementGroupID
        [String]$platformFaultDomain
        [String]$platformUpdateDomain

        [NetworkInterface[]]$NetworkInterfaces

        $RawResponse
    }

    $json = Invoke-MetadataService -URL $url
    $notes = $json | ConvertFrom-Json
    
    [InstanceMetadata]$metadata = New-Object InstanceMetadata
    $metadata.location = $notes.compute.location
    $metadata.name = $notes.compute.name
    $metadata.offer = $notes.compute.offer
    $metadata.osType = $notes.compute.osType
    $metadata.placementGroupId = $notes.compute.placementGroupId
    $metadata.platformFaultDomain = $notes.compute.platformFaultDomain
    $metadata.platformUpdateDomain = $notes.compute.platformUpdateDomain
    $metadata.publisher = $notes.compute.publisher
    $metadata.resourceGroupName = $notes.compute.resourceGroupName
    $metadata.sku = $notes.compute.sku
    $metadata.subscriptionId = $notes.compute.subscriptionId
    $metadata.tags = $notes.compute.tags
    $metadata.version = $notes.compute.version
    $metadata.vmId = $notes.compute.vmId
    $metadata.vmSize = $notes.compute.vmSize

    $metadata.NetworkInterfaces += ($notes.network.interface | ForEach-Object {
            $interface = $_
        
            Write-Verbose ($interface.ipv4.subnet[0]).address
            Write-Verbose $interface.ipv4.subnet[0].prefix
            Write-Verbose $interface.ipv4.ipAddress.privateIPAddress
            Write-Verbose $interface.ipv4.ipAddress.publicIPAddress
            Write-Verbose $interface.macAddress
        
            [NetworkInterface]$nic = New-Object NetworkInterface
            $nic.macAddress = $interface.macAddress
            $nic.ip4PublicAddress = $interface.ipv4.ipAddress.publicIPAddress
            $nic.ip4PrivateAddress = $interface.ipv4.ipAddress.privateIPAddress
            $nic.ip4SubnetPrefix = $interface.ipv4.subnet[0].prefix
            $nic.ip4SubnetAddress = $interface.ipv4.subnet[0].address

            $nic
        }
    )

    $metadata.RawResponse = $notes
        
    $metadata

}


Function Get-ManagedIdentityToken {
    [CmdletBinding()]    
    param(
        $apiVersion = '2018-02-01',
        $hostName = '169.254.169.254',
        $uri = 'metadata/identity/oauth2/token',
        $resource = 'https://management.azure.com/',
        $objectId = $null,
        $clientId = $null
    )   

    $url = "http://$hostName/$($uri)?api-version=$apiVersion&resource=$resource"
    
    if($null -ne $objectId){
        $url += "&object_id=$clientId"
    }

    if($null -ne $clientId){
        $url += "&client_id=$clientId"
    }

    $response = Invoke-MetadataService -URL $url
    $response | ConvertFrom-Json

    class TokenResponse{
        [String]$accessToken
        [String]$refreshToken
        [String]$expiresIn
        [String]$expiresOn
        [String]$notBefore
        [String]$resource
        [String]$tokenType

        $RawResponse
    }

    [TokenResponse]$tokenResponse = New-Object TokenResponse
    $tokenResponse.accessToken = $response.access_token
    $tokenResponse.refreshToken = $response.refresh_token
    $tokenResponse.expiresIn = $response.expires_in
    $tokenResponse.expiresOn = $response.expires_on
    $tokenResponse.notBefore = $response.not_before
    $tokenResponse.resource = $response.resource
    $tokenResponse.tokenType = $response.token_type
    $tokenResponse.RawResponse = $response

}

Function Invoke-MetadataService {
    [CmdletBinding()]
    param(    
        [String]$URL
    )

    Write-Verbose "Invoke-MetadataService:Requesting:URL: '$URL'"

    $response = Invoke-WebRequest -Uri $URL -UseBasicParsing -Headers @{"Metadata" = "true"}
    if ($response -eq $null) {
        Write-Error "Unable to get a response, no metadata for you"
    }
    else {
        Write-Verbose "Invoke-MetadataService:Success"
        $response.content
    }
    
}


Export-ModuleMember -Function Get-ManagedIdentityToken,Get-VMMetadata