AWS_Tools_AddOns.psm1

# Updated
function Get-S3Folder () {
    Param(
        [Parameter(Mandatory = $true )]
        [String]$BucketName,
        [String]$Prefix,
        [switch]$Files,
        [switch]$Folders,
        [Int]$MaxServiceCallHistory=50
    )

    If ($MaxServiceCallHistory) {
        Set-AWSHistoryConfiguration -MaxServiceCallHistory $MaxServiceCallHistory
    }

    $result = [System.Collections.Generic.List[PSObject]]::New()

    $Prefix += ($Prefix.EndsWith("/")) ? "" : "/"
    $s3Files = Get-S3Object -BucketName $BucketName -Prefix $Prefix -Delimiter '/'
    $s3Folders = ($AWSHistory.LastCommand.Responses.History).CommonPrefixes
    if ($Files) {
        foreach ($s3File in $s3Files) {
            $result.add(
                [PSCustomObject]@{
                    Key = $S3File.Key
                }
            )
        }
    } elseif ($Folders) {
        foreach ($s3Folder in $s3Folders) {
            $result.Add(
                [PSCustomObject]@{
                    Key = $S3Folder
                }
            )
        }
    } else {
        foreach ($s3File in $s3Files) {
            $result.add(
                [PSCustomObject]@{
                    Key = $S3File.Key
                }                
            )
        }
        foreach ($s3Folder in $s3Folders) {
            $result.Add(
                [PSCustomObject]@{
                    Key = $S3Folder
                }
            )
        }
    }
    return $result.ToArray() | Sort-Object -Property Key
    <#
    .SYNOPSIS
    List S3 Folders
    .DESCRIPTION
    This function emulates working with folders "Common prefixes" in S3. It will list the files and top level prefixes for a given bucket and prefix.
    .PARAMETER BucketName
    The name of the bucket
    .PARAMETER Prefix
    The prefix to list.
    .PARAMETER Files
    Only return the files in the top level prefix.
    .PARAMETER Folders
    Only return the folders in the top level prefix
    .PARAMETER MaxServiceCallHistory
    To get the common prefixes we call the $AWSHistory.LastCommand. By default that only returns the last 10 commands.
    So we set this to 50 as our default. This is usually fine for most uses unless you have a prefix with lot of sub-prefixes and files.
    .OUTPUTS
    An array of prefixes and/or files.
    #>

}

function Restore-S3Folder () {
    Param(
        [Parameter(Mandatory)]
        [string]$BucketName,
        [Parameter(Mandatory)]
        [string]$Prefix,
        [Parameter(Mandatory)]
        [int]$CopyLifetime,
        [Parameter(Mandatory)]
        [ValidateSet("Standard", "Expedited","Bulk")]
        [string]$Tier
    )
    #Validate the bucket exists
    $Bucket = Get-S3Bucket -BucketName $BucketName
    If (-not $Bucket) {
        Write-Host "Bucket not found!" -ForegroundColor Red
        exit
    }
    $Prefix += ($Prefix.EndsWith("/")) ? "" : "/"

    $s3Keys = Get-S3Object -BucketName $BucketName -Prefix $Prefix
    if ($s3Keys) {
        foreach ($S3Key in $s3Keys) {
            $msg = "Restoring object {0} to {1} for {2} days" -f $s3Key.Key, $Tier, $CopyLifetime
            Write-Host $msg -ForegroundColor Yellow
            $S3Key | Restore-S3Object -CopyLifetimeInDays $CopyLifetime -Tier $Tier -ErrorAction SilentlyContinue | Out-Null
        }
    }
    <#
    .SYNOPSIS
    Restore an S3 folder, i.e. "common prefix", from Glacier.
    .DESCRIPTION
    AWS Powershell Tools for S3 only has the ability to restore a single s3 object from glacier.
    This function allows you to restore all object with a common prefix.
    .PARAMETER BucketName
    The bucket name.
    .PARAMETER Prefix
    The Prefix to restore.
    .PARAMETER CopyLifetime
    The Number of days to keep the restored objects before returning them to glacier.
    .PARAMETER Tier
    The storage tier to restore the objects to. Valid entries are Standard, Expedited, Bulk
    .OUTPUTS
    Response indicating success or failure.
    #>

} 

Function Get-S3RestoreProgress() {
    Param(
        [Parameter(Mandatory)]
        [String]$BucketName,
        [Parameter(ParameterSetName='prefix')]
        [string]$Prefix,
        [Parameter(ParameterSetName='key')]
        [string]$Key
    )
    #validate bucket name
    $Bucket = Get-S3Bucket -BucketName $BucketName
    If (-not $Bucket) {
        Throw "Bucket not found"
    }
    if ($Key) {
        Get-S3ObjectMetadata -BucketName $BucketName -Key $key | Select-Object @{Name="Key";Expression={$Key}},RestoreInProgress, RestoreExpiration
    } else {
        $Prefix += ($Prefix.EndsWith("/")) ? "" : "/"

        $s3Keys = Get-S3Object -BucketName $BucketName -Prefix $Prefix

        
        $S3Keys |Foreach-Object {
            # Remove any by-directional unicode characters the stupid users may have entered into the file path & name
            $_.Key = $_.Key -cReplace '\P{IsBasicLatin}'
            $MetaData = $_ | Get-S3ObjectMetadata             
            $Progress = [PSCustomObject]@{
                Key = $_.Key
                RestoreInProgress = $MetaData.RestoreInProgress
                RestoreExpiration = $MetaData.RestoreExpiration
            }
            Write-Output $Progress
        } 

    }
    
    <#
    .SYNOPSIS
    Display the progress of a Glacier Restore.
    .PARAMETER BucketName
    The bucket name.
    .PARAMETER Prefix
    The prefix to check the restore progress. Required if Key is omitted.
    .PARAMETER Key
    The full key of an object to check. Required if Prefix is omitted.
    .OUTPUTS
    An array of AWS S3 Metadata objects. Check the RestoreInProgress property. A value of false indicates the restore has completed.
    #>

}

function Get-IAMGroupPermissions() {
    Param(
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [string]$GroupName
    )

    $IamGroup = (Get-IAMGroup -GroupName $GroupName).Group 


    $Group = [PsCustomObject]@{
        Arn = $IamGroup.Arn
        GroupName = $IamGroup.GroupName
        GroupId = $IamGroup.GroupId
        CreateDate = $IamGroup.CreateDate
        Policies = [List[PsObject]]::New()
    }
    
    # Get any inline policies

    $GroupInlinePolicies = Get-IAMGroupPolicyList -GroupName $GroupName
    if ($GroupInlinePolicies) {            
        foreach ($GroupInlinePolicy in $GroupInLinePolicies) {
            $iamPolicy = Get-IAMGroupPolicy -PolicyName $GroupInlinePolicy -GroupName $GroupName
            $PolicyDocument = [HttpUtility]::UrlDecode($IamPolicy.PolicyDocument) | ConvertFrom-Json

            $Policy = [PsCustomObject]@{
                PolicyName = $IamPolicy.PolicyName
                Arn = "N/A"
                PolicyDocument = $PolicyDocument
                PolicyType = 'Inline'
            }
            $Group.Policies.Add($Policy)
        }
    }

    # Get any Group Attached Policies.
    $GroupAttachedPolicies = Get-IAMAttachedGroupPolicyList -GroupName $GroupName
    if ($GroupAttachedPolicies) {
        foreach($GroupAttachedPolicy in $GroupAttachedPolicies) {
            $IamPolicy = Get-IamPolicy -PolicyArn $GroupAttachedPolicy.PolicyArn
            if ($IamPolicy.arn -like "*aws:policy*") {
                $PolicyTYpe = 'AWS Managed'
            } else {
                $PolicyType = 'Customer Managed'
            }
            # Get the default version Id
            $VersionId = (Get-IAMPolicyVersions -PolicyArn $IamPolicy.Arn | Where-Object {$_.IsDefaultVersion -eq $True}).VersionId
            $PolicyDocument = [HttpUtility]::UrlDecode((Get-IAMPolicyVersion -PolicyArn $IamPolicy.Arn -VersionId $VersionId).Document)
            $Policy = [PsCustomObject]@{
                PolicyName = $IamPolicy.Name
                Arn = $IamPolicy.Arn
                PolicyDocument = $PolicyDocument.ToString()
                PolicyType = $PolicyType
            }
            $Group.Policies.Add($Policy)
        }
    }

    return $Group

    <#
    .SYNOPSIS
    Returns the permissions assigned to this group.
    .DESCRIPTION
    Returns an object containing the permissions and policies assigned to a group.
    .PARAMETER GroupName
    The name of the group.
    .OUTPUTS
    An object with the following properties.
    .NOTES
    The Group Object contains the following properties.

    Name Type
    --------------- -------------
    Arn String
    GroupName String
    GroupId String
    CreateData DateTime
    Policies Collection

    The Policies property contains a collection of Policy objects with the following properties.

    Name Type
    --------------- ----------------------
    PolicyName String
    Arn String (Applicable for managed policies only)
    PolicyDocument PsObject
    PolicyType String (either 'inline', 'AWSManaged', or 'Customer Managed')

    The PolicyDocument (and the role AssumedRolePolicyDocument) have the following properties

    Name Type
    ------------------- -------------------
    Version String
    Statement PsObject(s) May be a single object or an array.

    The Statement Object has the following Properties

    Name Type
    -------------------- -------------------
    Sid String
    Effect String (either Allow or Deny)
    Actions Array of String objects
    Resource String
    #>

}

function Get-IAMUserPermissions() {
    [CmdletBinding()]
    Param(
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [string]$Username
    )

    # Get the IAM User
    $IamUser = Get-IAMUser -UserName $Username

    # Create a PSObject for the User Permissions
    $UserPermissions = [psCustomObject]@{
        Arn = $IamUser.Arn
        CreateDate = $IamUser.CreateDate
        PasswordLastUsed = $IamUser.PasswordLastUsed
        UserId = $IamUser.UserId
        UserName = $IamUser.UserName
        Policies = [List[PsObject]]::New()
        Groups = [List[PsObject]]::New()
    }

    #Get any inline policies assigned to the user.
    $UserInlinePolicies = Get-IamUserPolicyList -UserName $IamUser.Username
    foreach ($UserInlinePolicy in $UserInlinePolicies) {
        $UserPolicy = Get-IAMUserPolicy -PolicyName $UserInlinePolicies.PolicyName -UserName $IamUser.UserName
        $PolicyDocument = [HttpUtility]::UrlDecode($UserPolicy.PolicyDocument)
        $Policy = [PsCustomObject]@{
                PolicyName = $IamPolicy.Name
                Arn = "N/A"
                PolicyDocument = $PolicyDocument
                PolicyType = 'Inline'
            }
        $UserPermissions.InlinePolicies.Add($Policy)
    }

    # Get any attached Policies for this user.
    $UserAttachedPolicies = Get-IAMAttachedUserPolicies -UserName $Username
    foreach ($UserAttachedPolicy in $UserAttachedPolicies) {
        $IamPolicy = Get-IAMPolicy -PolicyArn $UserAttachedPolicy.PolicyArn
        if ($IamPolicy.Arn -like "*aws:policy") {
            $PolicyType | Add-Member -MemberType NoteProperty -Name "PolicyType" -Value "AWS Managed"
        } else {
            $PolicyType | Add-Member -MemberType NoteProperty -Name "MemberType" -Value "Customer Managed"
        }
        $PolicyDocument = [HttpUtility]::UrlDecode($IamPolicy.PolicyDocument)
        $Policy = [PsCustomObject]@{
                PolicyName = $IamPolicy.Name
                Arn = $IamPolicy.Arn
                PolicyDocument = $PolicyDocument.ToString()
                PolicyType = $PolicyType
            }
        UserPermissions.Policies.Add($Policy)
    }
    
    # Get any Groups that the user is a member of.
    $UserGroups = Get-IAMGroupForUser -UserName $IamUser.Username
    foreach ($UserGroup in $UserGroups) {   
        $Group = Get-IAMGroupPermissions -GroupName $UserGroup.GroupName
        $UserPermissions.Groups.Add($Group)
    }

    return $UserPermissions

    <#
    .SYNOPSIS
    Returns permissions assigned to a user.
    .DESCRIPTION
    Returns an object containing user information and the permissions and policies assigned to a user.
    Also contains a collection of group objects the User is a member of (See Get-IAMGroupPermissions).
    .PARAMETER Username
    The Name of the user.
    .OUTPUTS
    An object containing user information and policies assigned to the user.
    .NOTES
    The User Object contains the following properties.

    Name Type
    ------------------- ----------------------
    Arn String
    Username String
    UserId String
    CreateDate DateTime
    PasswordLastUsed DateTime
    Policies Collection of Policies assigned to the group.
    Groups Groups the user is a member of (Group Objects, see Get-IAMGroupPermissions)

    The Policy objects in the Policies collection have the following properties.

    Name Type
    ------------------- ---------------------
    PolicyName String
    Arn String (Applicable for managed policies only)
    PolicyDocument PsObject
    PolicyType String (either 'inline', 'AWSManaged', or 'Customer Managed')

    The PolicyDocument (and the role AssumedRolePolicyDocument) have the following properties

    Name Type
    ------------------- -------------------
    Version String
    Statement PsObject(s) May be a single object or an array.

    The Statement Object has the following Properties

    Name Type
    -------------------- -------------------
    Sid String
    Effect String (either Allow or Deny)
    Actions Array of String objects
    Resource String
    #>

}

function Get-IAMRolePermissions() {
    Param(
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [string]$RoleName
    )

    $IamRole = Get-IAMRole -RoleName $RoleName

    $AssumeRolePolicyDocument = [HttpUtility]::UrlDecode($IamRole.AssumeRolePolicyDocument) | ConvertFrom-Json

    $Role = [PSCustomObject]@{
        Arn = $IamRole.Arn
        RoleName = $IamRole.RoleName
        RoleId = $IamRole.RoleId
        RoleLastUsed = $IamRole.RoleLastUsed
        CreateDate = $IamRole.CreateDate
        MaxSessionDuration = $IamRole.MaxSessionDuration
        AssumeRolePolicyDocument = $AssumeRolePolicyDocument
        Policies = [List[PsObject]]::New()
    }

    # Get inline policies

    $RoleInlinePolicies = Get-IAMRolePolicies -RoleName $RoleName
    If ($RoleInlinePolicies) {
        foreach ($RoleInlinePolicy in $RoleInlinePolicies) {
            $IamPolicy = Get-IAMRolePolicy -PolicyName $RoleInlinePolicy -RoleName $RoleName
            $PolicyDocument = [HttpUtility]::UrlDecode($IamPolicy.PolicyDocument) | ConvertFrom-Json

            $Policy = [PSCustomObject]@{
                PolicyName = $IamPolicy.PolicyName
                Arn = "N/A"
                PolicyDocument = $PolicyDocument
                PolicyType = 'InLine'
            }
            $Role.Policies.Add($Policy)
        }
    }
    
    # Get any attached Policies
    $RoleAttachedPolicies = Get-IAMAttachedRolePolicies -RoleName $RoleName
    if ($RoleAttachedPolicies) {
        foreach ($RoleAttachedPolicy in $RoleAttachedPolicies) {
            $IamPolicy = Get-IamPolicy -PolicyArn $RoleAttachedPolicy.PolicyArn
            if ($IamPolicy.arn -like "*aws:policy*") {
                $PolicyType = 'AWS Managed'
            } else {
                $PolicyType = 'Customer Managed'
            }
            $VersionId = (Get-IAMPolicyVersions -PolicyArn $IamPolicy.Arn | Where-Object {$_.IsDefaultVersion -eq $True}).VersionId
            $PolicyDocument = [HttpUtility]::UrlDecode((Get-IAMPolicyVersion -PolicyArn $IamPolicy.Arn -VersionId $VersionId).Document) | ConvertFrom-Json
            $Policy = [PSCustomObject]@{
                PolicyName = $IamPolicy.PolicyName
                Arn = $IamPolicy.Arn
                PolicyDocument = $PolicyDocument
                $PolicyType = $PolicyType
            }
            $Role.Policies.Add($Policy)
        }
    }

    return $Role

    <#
    .SYNOPSIS
    Returns the permissions assigned to a role.
    .DESCRIPTION
    Returns an object containing the permissions and policies assigned to a role.
    .PARAMETER RoleName
    The name of the role.
    .OUTPUTS
    A role permissions object.
    .NOTES
    The Role permissions object has the following properties.

    Name Type
    --------------------------- ----------------
    Arn String
    RoleName String
    RoleId String
    CreateDate DateTime
    MaxSessionDuration Integer
    AssumeRolePolicyDocument PsObject
    Policies Collection of Policies assigned to the role.

    The Policy objects in the Policies collection have the following properties.

    Name Type
    ------------------- -------------------
    PolicyName String
    Arn String (Applicable for managed policies only)
    PolicyDocument PsObject
    PolicyType String (either 'inline', 'AWSManaged', or 'Customer Managed')

    The PolicyDocument (and the role AssumedRolePolicyDocument) have the following properties

    Name Type
    ------------------- -------------------
    Version String
    Statement PsObject(s) May be a single object or an array.

    The Statement Object has the following Properties

    Name Type
    -------------------- -------------------
    Sid String
    Effect String (either Allow or Deny)
    Actions Array of String objects
    Resource String
    #>

}

function Get-EC2InstanceList() {
    [CmdletBinding()]
    Param(
        [string]$Name,
        [string]$INstanceId,
        [string]$ProfileName,
        [switch]$NoProgress
    )

    If ($ProfileName) {
        try {
            Set-AWSCredential -ProfileName $ProfileName
        } catch {
            throw $_
        }
    }

    # Get accociated data and create indexed lists

    $Ec2Subnets = @{}
    Get-EC2Subnet | ForEach-Object {
        $Ec2Subnets.Add($_.SubnetId, $_)
    }

    $EC2INstanceTypes = @{}
    Get-EC2InstanceType | Foreach-Object {
        $EC2INstanceTypes.Add($_.INstanceType, $_)
    }

    $EC2InstanceStatus = @{}
    Get-EC2InstanceStatus | ForEach-Object {
        $EC2InstanceStatus.Add($_.Instanceid, $_)
    }


    $EC2InstanceList = [List[psObject]]::New()
    
    if ($INstanceId) {
        [array]$EC2Instances = (Get-EC2Instance -InstanceId $InstanceId).Instances 
    } elseIf ($Name) {
        [array]$EC2INstances = (Get-EC2Instance).Instances | Where-Object {$_.Tags[$_.Tags.Key.IndexOf("Name")].Value -eq $Name}
    } else {
        [array]$EC2Instances = (Get-EC2instance).Instances
    }
        
    $AccountId = (Get-STSCallerIdentity).Account
    
    foreach ($EC2Instance in $EC2INstances) {        
        $Tags = $EC2Instance.Tags
        $Name = $Tags[$Tags.Key.IndexOf("Name")].Value
        
        $State = $EC2Instance.State.Name
        $AvailabilityZone = $EC2InstanceStatus[$EC2Instance.InstanceId].AvailabilityZone  #(Get-EC2InstanceStatus -InstanceId $EC2Instance.InstanceId -IncludeAllInstance $true).AvailabilityZone
        $SecurityGroup = $EC2INstance.SecurityGroups.GroupName -join ","
        $SubnetTags = $Ec2Subnets[$EC2Instance.SubnetId].Tags #(Get-EC2Subnet -SubnetId $EC2Instance.SubnetId).Tags
        if ($SubnetTags) {
            $SubnetName = $SubnetTags[$SubnetTags.Key.IndexOf("Name")].Value
        } else {
            $SubnetName = $null
        }
        
        $InstanceType = $EC2INstanceTypes[$EC2INstance.InstanceType] #Get-EC2InstanceType -InstanceType $EC2Instance.InstanceType
        $ProcessorMfr = $InstanceType.ProcessorInfo.Manufacturer
        $ProcessorArchitectures = $InstanceType.ProcessorInfo.SupportedArchitectures
        $ProcessorFeatures = $InstanceType.ProccessorInfo.SupportedFeatures
        $ProcessorClockSpeed = $InstanceType.ProcessorInfo.SustainedClockSpeedInGhz
        if ($InstanceType.InstanceStorageSupported) {
            $InstanceStorageSupported = $InstanceType.InstanceStorageSupported
            $InstanceStorageNvmeSupport = $InstanceType.InstanceStorageInfo.NvmeSupport
            $InstanceStorageEncryption = $InstanceType.InstanceStorageInfo.EncryptionSupport
            $InstanceStorageSize = $InstanceType.InstanceStorageInfo.TotalSizeInGB
            $InstanceStorageType = $InstanceType.InstanceStorageInfo.Disks.Type
        }
        $Memory = $InstanceType.memoryInfo.SizeInMiB / 1024
        $EnaSupported = $InstanceType.NetworkInfo.EnaSupport
        $NetworkPerformance = $InstanceType.NetworkInfo.NetworkPerformance

        $Instance = [PSCustomObject]@{
            Name = $Name
            InstanceId = $EC2Instance.InstanceId
            InstanceState = $State
            InstanceType = $EC2Instance.InstanceType
            ImageId = $EC2Instance.ImageId
            AvailabilityZone = $AvailabilityZone
            SecurityGroup = $SecurityGroup
            KeyName = $EC2Instance.KeyName
            PrivateIpAddress = $EC2Instance.PrivateIpAddress
            PrivateDnsName = $EC2Instance.PrivateDnsName
            PublicIPAddress = $EC2Instance.PublicIpAddress
            PublicDNSName = $EC2Instance.PublicDnsName
            SubnetId = $EC2Instance.SubnetId
            Subnet = $SubnetName
            LaunchTime = $EC2Instance.LaunchTime
            Platform = $EC2Instance.PlatformDetails
            Instance = $EC2Instance
            ProcessorMgr = $ProcessorMfr
            ProcessorArchitecture = $ProcessorArchitectures
            ProcessorFeatures = $ProcessorFeatures
            ProcessorClockSpeed = $ProcessorClockSpeed
            InstanceStorageSupported = $InstanceStorageSupported
            InstanceStorageNvmeSupport = $InstanceStorageNvmeSupport
            InstanceStorageEncryption = $InstanceStorageEncryption
            InstanceStorageSize = $InstanceStorageSize
            InstanceStorageType = $InstanceStorageType
            Memory = $Memory
            EnaSupported = $EnaSupported
            NetworkPerformance = $NetworkPerformance
            AccountId = $AccountId
        }


        $EC2InstanceList.Add($Instance)
    }
    return $EC2InstanceList.ToArray()
    <#
    .SYNOPSIS
    Returns a list of EC2 Instances
    .DESCRIPTION
    REturns a list of EC2 INstances with relevant properties.
    .PARAMETER ProfileName
    The saved EC2 profile to used to retrieve the data.
    .PARAMETER IncludeAccountId
    Include the account ID the EC2 INstance is in.
    .PARAMETER NoProgress
    Do not show the progress indicator.
    .OUTPUTS
    A collection of custom EC2 Instances.
    .NOTES
    Each EC2 instance object has the following properties.

    Name Type Description
    ------------------- --------- -------------------------
    Name String Name of the Instance.
    InstanceId String Instance identifier.
    InstanceState String The state of the instance (Stopped or Running).
    InstanceType String The Instance type.
    AvailabilityZone String The availability zone the instance is in.
    SecurityGroup String The Security Groups assigned to the Instance.
    KeyName String The KMS Key assigned to the Instance.
    PrivateIpAddress String The Private IP address assigned to the Instance.
    PrivateDNSName String The Private DNS Name assigned to the instance.
    PublicIPAddress String The Public IP address assigned tot he instance.
    PublicDNSName String The Public DNS name assigned to the instance.
    SubnetId String The Subnet Assigned to the instance.
    Subnet String The Subnet Name.
    Platform String The Instance Platform i.e Windows Linux
    #>

}

function Get-DiskMappings() {
    
    function Convert-SCSITargetIdToDeviceName {
        param(
            [int]$SCSITargetId
        )

        If ($SCSITargetId -eq 0) {
          return "sda1"
        }
        $deviceName = "xvd"
        If ($SCSITargetId -gt 25) {
          $deviceName += [char](0x60 + [int]($SCSITargetId / 26))
        }
        $deviceName += [char](0x61 + $SCSITargetId % 26)
        return $deviceName
    }
      
    # [string[]]$array1 = @()
    # [string[]]$array2 = @()
    # [string[]]$array3 = @()
    # [string[]]$array4 = @()
    
    $Win32_Volumes = Get-CimInstance -ClassName Win32_Volume

    $VolNames = $Win32_Volumes.Name
    $VolIDs = $Win32_Volumes.DeviceID

    # Get-CimInstance -ClassName Win32_Volume | Select-Object Name, DeviceID | ForEach-Object {
    # $array1 += $_.Name
    # $array2 += $_.DeviceID
    # }
    
    $Serials = [List[PsObject]]::New()
    $FriendlyNames = [List[PsObject]]::New()

    foreach ($VolID in $VolIDs) {
        Write-Host "Getting disk information for volume $VolId" -ForegroundColor Green
        $disk = Get-Volume -Path $VolID | Get-Partition | Get-Disk
        $Serials.Add(($Disk.SerialNumber -replace "_[^ ]*$" -replace "vol", "vol-"))
        $FriendlyNames.Add($Disk.FriendlyName)
    }
    # $i = 0
    # While ($i -ne ($array2.Count)) {
    # $array3 += ((Get-Volume -Path $array2[$i] | Get-Partition | Get-Disk).SerialNumber) -replace "_[^ ]*$" -replace "vol", "vol-"
    # $array4 += ((Get-Volume -Path $array2[$i] | Get-Partition | Get-Disk).FriendlyName)
    # $i ++
    # }

    $DiskInfo = @{}
    foreach ($VolName in $VolNames) {
        $i = $VolNames.IndexOf($VolName)
        $Drive = [PSCustomObject]@{
            DriveLetter = ($VolName.replace(':\\', ''))
            VolumeId = $VolIDs[$i]
            Serial = $Serials[$i]
            FriendlyName = $FriendlyNames[$i]
        }
        $DiskInfo.Add($VolName.replace(':\',''), $Drive)
    }
    
    # [array[]]$array = $VolNames, $VolIds, $Serials, $FriendlyNames
    
    Try {
        $InstanceId = Get-EC2InstanceMetadata -Category "InstanceId"
        $Region = Get-EC2InstanceMetadata -Category "Region" | Select-Object -ExpandProperty SystemName
    }
    Catch {
        Write-Host "Could not access the instance Metadata using AWS Get-EC2InstanceMetadata CMDLet.
        Verify you have AWSPowershell SDK version '3.1.73.0' or greater installed and Metadata is enabled for this instance."
 -ForegroundColor Yellow
    }
    Try {
        $BlockDeviceMappings = (Get-EC2Instance -Region $Region -Instance $InstanceId).Instances.BlockDeviceMappings
        [array]$VirtualDeviceMap = (Get-EC2InstanceMetadata -Category "BlockDeviceMapping").GetEnumerator() | ForEach-Object {$_}
    }
    Catch {
        Write-Host "Could not access the AWS API, therefore, VolumeId is not available.
        Verify that you provided your access keys or assigned an IAM role with adequate permissions."
 -ForegroundColor Yellow
    }
    
    Get-disk | ForEach-Object {
        $DriveLetter = $null
        $VolumeName = $null
        $VirtualDevice = $null
        $DeviceName = $_.FriendlyName
      
        $DiskDrive = $_
        $Disk = $_.Number
        $Partitions = $_.NumberOfPartitions
        $EbsVolumeID = $_.SerialNumber -replace "_[^ ]*$" -replace "vol", "vol-"
        if ($Partitions -ge 1) {
          $PartitionsData = Get-Partition -DiskId $_.Path
          $DriveLetter = $PartitionsData.DriveLetter | Where-object { $_ -notin @("", $null) }
          $VolumeName = (Get-PSDrive | Where-Object { $_.Name -in @($DriveLetter) }).Description | Where-object { $_ -notin @("", $null) }
        }
        If ($DiskDrive.path -like "*PROD_PVDISK*") {
          $BlockDeviceName = Convert-SCSITargetIdToDeviceName((Get-CIMInstance -ClassName Win32_Diskdrive | Where-Object { $_.DeviceID -eq ("\\.\PHYSICALDRIVE" + $DiskDrive.Number) }).SCSITargetId)
          $BlockDeviceName = "/dev/" + $BlockDeviceName
          $BlockDevice = $BlockDeviceMappings | Where-Object { $BlockDeviceName -like "*" + $_.DeviceName + "*" }
          $EbsVolumeID = $BlockDevice.Ebs.VolumeId
          # $VirtualDevice = ($VirtualDeviceMap.GetEnumerator() | Where-Object { $_.Value -eq $BlockDeviceName }).Key | Select-Object -First 1
          $VirtualDevice = $VirtualDeviceMap.Where({$_.Value -eq $BlockDeviceName}).Key | Select-Object -First 1
        }
        ElseIf ($DiskDrive.path -like "*PROD_AMAZON_EC2_NVME*") {
          $BlockDeviceName = Convert-SCSITargetIdToDeviceName((Get-CIMInstance -ClassName Win32_Diskdrive | Where-Object { $_.DeviceID -eq ("\\.\PHYSICALDRIVE" + $DiskDrive.Number) }).SCSITargetId)
          $BlockDevice = $null
          #$VirtualDevice = ($VirtualDeviceMap.GetEnumerator() | Where-Object { $_.Value -eq $BlockDeviceName }).Key | Select-Object -First 1
          $VirtualDevice = $VirtualDeviceMap.Where({$_.Value -eq $BlockDeviceName}).Key | Select-Object -First 1
        }
        ElseIf ($DiskDrive.path -like "*PROD_AMAZON*") {
          if ($DriveLetter -match '^[a-zA-Z0-9]') {
            $DeviceName = $DiskInfo["$DriveLetter"].FriendlyName
            # $i = 0
            # While ($i -ne ($array3.Count)) {
            # if ($array[2][$i] -eq $EbsVolumeID) {
            # $DriveLetter = $array[0][$i]
            # $DeviceName = $array[3][$i]
            # }
            # $i ++
            # }
          }
          $BlockDevice = ""         
          $BlockDeviceName = ($BlockDeviceMappings | Where-Object { $_.ebs.VolumeId -eq $EbsVolumeID }).DeviceName
          $VirtualDevice = $VirtualDeviceMap.Where({$_.Value -eq $BlockDeviceName}).Key | Select-Object -First 1
        }
        ElseIf ($DiskDrive.path -like "*NETAPP*") {
          if ($DriveLetter -match '^[a-zA-Z0-9]') {
            $DeviceName = $DiskInfo["$DriveLetter"].FriendlyName
            # $i = 0
            # While ($i -ne ($array3.Count)) {
            # if ($array[2][$i] -eq $EbsVolumeID) {
            # $DriveLetter = $array[0][$i]
            # $DeviceName = $array[3][$i]
            # }
            # $i ++
            # }
          }
          $EbsVolumeID = "FSxN Volume"
          $BlockDevice = ""
          $BlockDeviceName = ($BlockDeviceMappings | Where-Object { $_.ebs.VolumeId -eq $EbsVolumeID }).DeviceName
        }
        Else {
          $BlockDeviceName = $null
          $BlockDevice = $null
        }
        New-Object PSObject -Property @{
          Disk          = $Disk;
          Partitions    = $Partitions;
          DriveLetter   = If ($null -eq $DriveLetter) { "N/A" } Else { $DriveLetter };
          EbsVolumeId   = If ($null -eq $EbsVolumeID) { "N/A" } Else { $EbsVolumeID };
          Device        = If ($null -eq $BlockDeviceName) { "N/A" } Else { $BlockDeviceName };
          VirtualDevice = If ($null -eq $VirtualDevice) { "N/A" } Else { $VirtualDevice };
          VolumeName    = If ($null -eq $VolumeName) { "N/A" } Else { $VolumeName };
          DeviceName    = If ($null -eq $DeviceName) { "N/A" } Else { $DeviceName };
        }
    } | Sort-Object Disk | Select-Object Disk, Partitions, DriveLetter, EbsVolumeId, Device, VirtualDevice, DeviceName, VolumeName 

    <#
    .SYNOPSIS
    LIst disk mappings on an EC2 Instance.
    .DESCRIPTION
    List the disk mappings on an EC2 instance to reference the volume ID with the Windows volume and drive letter.
    This function must be run on an EC2 Windows instance.
    .OUTPUTS
    An array of disk objects.
    #>

}

function Set-SecretVault() {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$VaultName,
        [ValidateSet('Password','None')]
        [string]$Authentication,
        [ValidateSet('Prompt','None')]
        [string]$Interaction
    )

    $Config = Get-SecretStoreConfiguration | Where-Object {$_.Scope -eq "CurrentUser"}

    $Params = @{}

    If ($Authentication) {
        if ($Authentication -ne $Config.Authentication) {
            $Params.Add("Authentication", $Authentication)
        }
    }

    if ($Interaction) {
        if ($Interaction -ne $Config.Interaction) {
            $Params.Add("Interaction", $Interaction)
        }
    }

    If ($Params.Count -gt 0) {
        Set-SecretStoreConfiguration -Scope CurrentUser @Params
    }

    Register-SecretVault -Name $VaultName -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault -AllowClobber

    Write-Host "Vault $VaultName Created"
    <#
    .DESCRIPTION
    Creates a Secrets vault and sets the option configuration parameters.
    Note: If you plan to use this vault for automation purposes you must set Authentication and Interaction to 'None'.
    .PARAMETER VaultName
    The Name of the vault.
    .PARAMETER Authentication
    The type of Authentication, Either 'Password' or 'None'
    .PARAMETER Interaction
    Allow or suppress user interaction. Either 'Prompt' or 'None'. If set to none and the vault requires a password an error will occur.
    .NOTES
    The powershell module SecretStore which this module uses does not support vaults outside the user scope. Therefor creating multiple vaults is redundant.
    SecretStore will just save secrets in all vaults. This is by design from Microsoft. It may change in the future.
    If you are using this vault for secure AWS Access keys you CANNOT set authentication to password. The credential_process functionality of the
    AWS credential process will hang waiting on the prompt and will not accept input from the console. You must set authentication and Interaction to 'none'.
    #>

}

function Set-SecureAWSCredentials() {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [String]$ProfileName,
        [string]$AccessKeyId,
        [Parameter(Mandatory)]
        [string]$SecretAccessKey,
        [string]$SessionToken,
        [Parameter(Mandatory)]
        [String]$Region,
        [datetime]$Expiration,
        [string]$VaultName
    )

    $Cred = Get-AWSCredential -ProfileName $ProfileName
    if ($Cred) {
        Remove-AWSCredentialProfile -ProfileName $ProfileName -Force
    }

    $secretIn = @{
    Version=1;
    AccessKeyId= $AccessKeyId;
    SecretAccessKey=$SecretAccessKey;
    } 

    if ($SessionToken) {
        $SecretIn.Add("SessionToken", $SessionToken)
    }

    if ($Expiration) {
        $ExpDate = $Expiration.ToString("yyyy-MM-dd HH:mm:ss")
        $SecretIn.Add("Expiration", $ExpDate)
    }

    $secret = $secretIn | ConvertTo-Json

    $Params = @{
        Name = $ProfileName
        Secret = $secret
    }
    
    if ($VaultName) {
        $Params.Add("Vault", $VaultName)
    }
    

    Set-Secret @Params

    $CredFile ="{0}/.aws/credentials" -f $home

    # Add-Content -Path $CredFile -Value "[$ProfileName]"
    if ($IsWindows) {
        $content = @"
[$ProfileName]
credential_process = credential_process.cmd "$ProfileName"
region = $Region
"@

        Add-Content -Path $CredFile -Value $content
    } else {
        $content = @"
[$ProfileName]
credential_process = credential_process.sh "$ProfileName"
region = $Region
"@

        Add-Content -Path $CredFile -Value $content
    }

    <#
    .SYNOPSIS
    Creates a secure entry in the aws credentials file.
    .DESCRIPTION
    Creates a secure entry in the AWS Credentials file. The AWS Keys are stored in a Secret vault created by Set-SecretVault.
    This credential entry uses a credential process. This process calls a script based on the Operating System.
    For Windows: credential_process.cmd
    For Linux/Mac: credential_process.sh
    Copy the appropriate file into a directory that is in the path.
    For Linux the best place is ~/.local/bin
    For Windows any directory that is in the path. For optimal security create a folder under the user profile and add that path to the User section of the Path Environment variable configuration.
    .PARAMETER ProfileName
    The name of the profile. To set a default profile you must name the profile default.
    .PARAMETER AccessKeyId
    The AWS Access Key ID.
    .PARAMETER SecretAccessKey
    The AWS Secret Access key.
    .PARAMETER SessionToken
    A Session Token for this credential.
    .PARAMETER Region
    The AWS Region for this profile.
    .PARAMETER Expiration
    An option expiration date, the stored secret will expire after this date/time.
    .PARAMETER VaultName
    An optional vault name. If omitted, the secret will be created in the default vault.

    #>

}