domainAwsPowershellTools.psm1

<#
Module Mixed by BarTender
    A Framework for making PowerShell Modules
    Version: 6.1.22
    Author: Adrian.Andersson
    Copyright: 2019 Domain Group

Module Details:
    Module: domainAwsPowershellTools
    Description: Bunch of scripts to help with Aws Powershell
    Revision: 1.0.1.1
    Author: Adrian.Andersson
    Company: Domain Group

Check Manifest for more details
#>


function get-dynamoDbQueryResult
{
    <#
        .SYNOPSIS
            Execute a query against DynamoDB
             
        .DESCRIPTION
            Run a DynamoDB Query
            Return some results
             
        .PARAMETER Tablename
            Name of the DynamoDB Table
        .PARAMETER key
            Name of the table key to look at
        .PARAMETER awsRegion
            Name of the awsRegion to use
        .PARAMETER value
            The value of the key we are looking for
         
        .PARAMETER items
            Number of items
        .EXAMPLE
            get-dynamoDbQueryResult -table my-dynamodb-table -key my-dynamodb-reference -value what-value-do-i-want -items 1
                    
             
             
        .NOTES
            Author: Adrian Andersson
            Last-Edit-Date: 2018-09-05
             
             
            Changelog:
                2018-09-05 - AA
                    - Initial Script
                    - This was a fun crash course in DynamoDB
                    - Reverse-Engineered Mostafa's provided query to use the dotNet class objects over the AWS CLI
                2019-03-15 - AA
                    - Rewrote to domainAwsPowershellTools
                     
                     
                    #>

    [CmdletBinding()]
    PARAM(
        [Parameter(Mandatory=$true)]
        [string]$Tablename,
        [Parameter(Mandatory=$true)]
        [string]$key,
        [Parameter(Mandatory=$true)]
        [string]$awsRegion,
        [Parameter(Mandatory=$true)]
        [string]$value,
        [Parameter(Mandatory=$false)]
        [int]$items = 5
    )
    begin{
        #Return the script name when running verbose, makes it tidier
        write-verbose "===========Executing $($MyInvocation.InvocationName)==========="
        #Return the sent variables when running debug
        Write-Debug "BoundParams: $($MyInvocation.BoundParameters|Out-String)"
        $regionName = $awsRegion
        
        $region = [Amazon.RegionEndpoint]::GetBySystemName($regionName)
        $dbClient = New-Object Amazon.DynamoDBv2.AmazonDynamoDBClient($region)
    }
    
    process{
        $req = New-object Amazon.DynamoDBv2.Model.QueryRequest
        $req.tableName = $tableName
        write-verbose "Invoking against table: $table"
        $req.KeyConditionExpression = "$key = :anything"
        $keyAttrObj = new-object Amazon.DynamoDBv2.Model.AttributeValue
        $keyAttrObj.S = $value
        write-verbose "Searching for value $($keyAttrObj.S)"
        $req.expressionAttributeValues = new-object 'System.Collections.Generic.Dictionary[string,Amazon.DynamoDBv2.Model.AttributeValue]'
        $req.expressionAttributeValues.add(':anything',$keyAttrObj.s)
        $req.limit = $items
        write-verbose "Limiting to $items resultCount"
        write-verbose "Executing the query"
        $qResult = $dbClient.query($req)
        $qItems = $qResult.items
        write-verbose "Found $($qItems.count) results"
        $i = 0
        foreach($result in $qItems)
        {
            $ht = @{}
            write-verbose "Parsing result for item $i"
            foreach($key in $result.keys)
            {
                $rValue = $result."$key"
                if($rValue.S)
                {
                    $ht."$key" = $rValue.S
                }elseIf($rValue.N){
                    $ht."$key" = $rValue.N
                }
            }
            $obj = [pscustomobject]$ht
            $obj
            write-verbose "Finished parsing value for item $i"
            $i++
            remove-item ht,obj,rValue -errorAction ignore
        }
        
    }
    
}

function get-instanceAmiDetails
{
    <#
        .SYNOPSIS
            get an ec2 instance image details
             
        .DESCRIPTION
            Get ec2 instance and ami details
            return an object
             
        .PARAMETER instanceId
            what instance do you need
             
        ------------
        .EXAMPLE
            get-instanceAmiDetails i-abc123456,i-xyz654321
         
        .EXAMPLE
            $instanceIds = @(
                'i-abc123456',
                'i-xyz654321'
            )
            get-instanceAmiDetails $instanceIds
             
      .NOTES
            Author: Adrian Andersson
            Last-Edit-Date: 2019-03-15
            Created On Request
             
             
            Changelog:
                2019-03-15 - AA
                    - Initial Script
                     
                     
        .COMPONENT
            What cmdlet does this script live in
    #>

    [CmdletBinding()]
    PARAM(
        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
        [string[]]$instanceId
    )
    begin{
        #Return the script name when running verbose, makes it tidier
        write-verbose "===========Executing $($MyInvocation.InvocationName)==========="
        #Return the sent variables when running debug
        Write-Debug "BoundParams: $($MyInvocation.BoundParameters|Out-String)"
        $awsOwnerIds = @('801119661308')
        
    }   
    
    process{
        foreach($instId in $instanceId)
        {   
            try{
                $instance = $(Get-EC2Instance $instId -ErrorAction Stop).instances[0]
            }catch{
                $null
            }
            
            if($instance)
            {
            
                $image = Get-EC2Image -ImageId $instance.imageId
                $hash = @{
                    instanceId = $instance.InstanceId
                    instanceIsWindows = if($instance.Platform.Value){$true}else{$false}
                    instanceType = $instance.instanceType.Value
                    instanceLaunchDate = $(get-date $instance.LaunchTime)
                }
                if($image)
                {
                    $hash.amiName = $image.name
                    $hash.amiState = $image.state.value
                    $hash.amiAwsOwned = $(if($image.ownerid -in $awsOwnerIds){$true}else{$false})
                    $hash.amiCreationDate = $(get-date $image.creationdate)
                }else{
                    $hash.amiName = 'Unknown'
                    $hash.amiState = 'Unknown'
                    $hash.amiAwsOwned = 'Unknown'
                    $hash.amiCreationDate = 'Unknown'
                }
                [psCustomObject]$hash
            }else{
                write-warning "InstanceId: $instId not found"
            }
        }
        
    }
    
}

function remove-s3DeleteMarker
{
    <#
        .SYNOPSIS
            OopsiePerson - 'Help, I broked S3, I accidentally deleted all our stuff'
            DevOpsPerson - 'Dont panic, you have versioning enabled'
             
        .DESCRIPTION
            For when somoene accidentally marks an entire bucket with a delete marker
            MAKE SURE you have versioning enabled
             
        .PARAMETER bucketname
            Name of the S3 bucket
        .PARAMETER prefix
            The s3 keyprefix to search
        .PARAMETER DaysToRewind
            How far back should we look for delete markers.
            I.e. How many days ago did this mistake happen
            Required so we don't remove versions that perhaps should be legitimately removed
        .EXAMPLE
            remove-s3DeleteMarker -bucketName 'mybucket' -prefix 'my/key/prefix'
             
            #### DESCRIPTION
            Remove any versions with delete markers within the default DaysToRewind (3)
             
        .NOTES
            Author: Adrian Andersson
            Company: Domain Group
            Last-Edit-Date: 2018/08/14
             
             
            Changelog:
                2018/08/14 - AA
                     
                    - Initial Script
                    - Added DaysToRewind after feedback
                    - Fixed pagination issue
                    - Added continue switch to proceed
                2019-03-15 - AA
                    - Rewrote to domainAwsPowershellTools
                     
    #>

    [CmdletBinding(SupportsShouldProcess=$True)]
    PARAM(
        [Parameter(Mandatory=$true)]
        [Alias("bucket")]
        [string]$bucketname,
        [Parameter(Mandatory=$true)]
        [Alias("keyprefix")]
        [string]$prefix,
        [ValidateRange(1,180)]
        [int]$DaysToRewind = 3,
        [switch]$continue
        
    )
    begin{
        #Flip the days
        
        $days = 0 - $daysToRewind
        $backDate = $(get-date).AddDays( $days)
        write-verbose "Date to remove deleteMarker from : $($backdate)"
        write-verbose 'Getting S3 Version Data'
        #Get a subset of the data to validate the bucket is ok
        $versionData = Get-S3Version -BucketName $bucketName -Prefix $Prefix -MaxKey 2
        if(!($versiondata) -or (!$versiondata.versions) -or ($versiondata.versions.count -lt 1))
        {
            throw 'Unable to retrieve version info. Bucket could be wrong or versions unavailable'
        }
        $keymarker = $null
        $more = $True
        $page = 0
        $versions = @{}
        while($more -eq $true)
        {
            write-verbose "Getting versions from page:$page"
            $versionData = Get-S3Version -BucketName $bucketName -Prefix $Prefix -KeyMarker $keyMarker
            
            $versions."$page" = $versionData.Versions
            if($versionData.NextKeyMarker)
            {
                write-verbose 'More data, incrementing Page'
                $page++
                $keyMarker=$versionData.NextKeyMarker
            }else{
                write-verbose 'No more data'
                $more = $false
            }
            
        }
        $allVersions = $($versions.Values|ForEach-Object{$_})
        write-verbose "Found data for $($allVersions.count) versions"
        $Deleted = $allVersions | where-object{$_.IsDeleteMarker -eq $true -and $_.LastModified -gt $backDate}
        write-verbose "Found data for $($Deleted.count) versions with delete markers"
        if($allVersions.count -eq $Deleted.count)
        {
            throw 'Number of versions is equal to number of deletes. Does this bucket have versioning?'
        }
        
    }
    
    process{
        write-warning "$($deleted.count) items of $($allversions.count) total items will be deleted)"
        if(!$continue)
        {
            $confirmRead = read-host 'Are you sure about this? (y)'
            if($confirmRead -eq 'y')
            {
                $continue = $true
            }else{
                return
            }
            
        }
        if($continue)        
        {
            foreach($file in $deleted)
            {
                if ($PSCmdlet.ShouldProcess("Remove key: $($file.Key) `tVersion: $($file.versionId) ")) {
                    remove-S3Object -bucketName $file.bucketName -Key $file.Key -VersionId $file.versionId -Confirm:$false
                }
            }
        }
    }
}