S3-Client.psm1
$AWS_PROFILE_PATH = "$HOME/.aws/" $AWS_CREDENTIALS_FILE = $AWS_PROFILE_PATH + "credentials" $AWS_CONFIG_FILE = $AWS_PROFILE_PATH + "config" # workarounds for PowerShell issues if ($PSVersionTable.PSVersion.Major -lt 6) { Add-Type @" using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult( ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } "@ # Using .NET JSON Serializer as JSON serialization included in Invoke-RestMethod has a length restriction for JSON content Add-Type -AssemblyName System.Web.Extensions $global:javaScriptSerializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer $global:javaScriptSerializer.MaxJsonLength = [System.Int32]::MaxValue $global:javaScriptSerializer.RecursionLimit = 99 } ### Helper Functions ### function ParseExceptionBody($Response) { if ($Response) { $Reader = New-Object System.IO.StreamReader($Response.GetResponseStream()) $Reader.BaseStream.Position = 0 $Reader.DiscardBufferedData() $ResponseBody = $reader.ReadToEnd() if ($ResponseBody.StartsWith('{')) { $ResponseBody = $ResponseBody | ConvertFrom-Json | ConvertTo-Json } return $ResponseBody } else { return $Response } } function ConvertTo-SortedDictionary($HashTable) { $SortedDictionary = New-Object 'System.Collections.Generic.SortedDictionary[string, string]' foreach ($Key in $HashTable.Keys) { $SortedDictionary[$Key]=$HashTable[$Key] } Write-Output $SortedDictionary } function Sign($Key,$Message) { $hmacsha = New-Object System.Security.Cryptography.HMACSHA256 $hmacsha.Key = $Key $hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Message)) } function GetSignatureKey($Key, $Date, $Region, $Service) { $SignedDate = sign ([Text.Encoding]::UTF8.GetBytes(('AWS4' + $Key).toCharArray())) $Date $SignedRegion = sign $SignedDate $Region $SignedService = sign $SignedRegion $Service sign $SignedService "aws4_request" } function ConvertFrom-AwsConfigFile { [CmdletBinding()] PARAM ( [parameter( Mandatory=$True, Position=0, HelpMessage="AWS Config File")][String]$AwsConfigFile ) Process { if (!(Test-Path $AwsConfigFile)) { throw "Config file $AwsConfigFile does not exist!" } $Content = Get-Content -Path $AwsConfigFile -Raw # remove empty lines $Content = $Content -replace "(`n$)*","" # convert to JSON structure $Json = $Content -replace "profile ","" -replace "`n([^\[])",',$1' -replace "\[","`"" -replace "],","`":{`"" -replace "\s*=\s*","`":`"" -replace ",","`",`"" -replace "`n","`"}," -replace "^","{" -replace "$","`"}}" # parse JSON to Hashtable $Parser = New-Object Web.Script.Serialization.JavaScriptSerializer $Parser.MaxJsonLength = $Json.length $Hashtable = $Parser.Deserialize($Json, [hashtable]) Write-Output $Hashtable } } function ConvertTo-AwsConfigFile { [CmdletBinding()] PARAM ( [parameter( Mandatory=$True, Position=0, HelpMessage="Config to store in config file")][hashtable]$Config, [parameter( Mandatory=$True, Position=1, HelpMessage="AWS Config File")][String]$AwsConfigFile ) Process { if (!(Test-Path $AwsConfigFile)) { New-Item -Path $AwsConfigFile -ItemType File -Force } $Output = "" foreach ($Key in $Config.Keys) { $Output += "[$Key]`n" foreach ($Value in $Config[$Key].Keys) { $Output += "$Value = $($Config[$Key][$Value])`n" } } $Output | Out-File -FilePath $AwsConfigFile -NoNewline } } ### AWS Cmdlets ### <# .SYNOPSIS Retrieve SHA256 Hash for Payload .DESCRIPTION Retrieve SHA256 Hash for Payload #> function Global:Get-AwsHash { [CmdletBinding()] PARAM ( [parameter( Mandatory=$False, Position=0, HelpMessage="String to hash")][String]$StringToHash ) Process { $hasher = [System.Security.Cryptography.SHA256]::Create() $Hash = ([BitConverter]::ToString($hasher.ComputeHash([Text.Encoding]::UTF8.GetBytes($StringToHash))) -replace '-','').ToLower() Write-Output $Hash } } <# .SYNOPSIS Create AWS Authentication Signature Version 2 for Request .DESCRIPTION Create AWS Authentication Signature Version 2 for Request #> function Global:New-AwsSignatureV2 { [CmdletBinding()] PARAM ( [parameter( Mandatory=$True, Position=0, HelpMessage="S3 Access Key")][String]$AccessKey, [parameter( Mandatory=$True, Position=1, HelpMessage="S3 Secret Access Key")][String]$SecretAccessKey, [parameter( Mandatory=$True, Position=2, HelpMessage="EndpointUrl")][String]$EndpointUrl, [parameter( Mandatory=$False, Position=3, HelpMessage="HTTP Request Method")][ValidateSet("OPTIONS","GET","HEAD","PUT","DELETE","TRACE","CONNECT")][String]$HTTPRequestMethod="GET", [parameter( Mandatory=$False, Position=4, HelpMessage="URI")][String]$Uri="/", [parameter( Mandatory=$True, Position=6, HelpMessage="Content MD5")][Hashtable]$ContentMD5, [parameter( Mandatory=$True, Position=7, HelpMessage="Content Type")][Hashtable]$ContentType, [parameter( Mandatory=$True, Position=8, HelpMessage="Date")][DateTime]$DateTime, [parameter( Mandatory=$False, Position=9, HelpMessage="Headers")][Hashtable]$Headers=@{} ) Process { # this Cmdlet follows the steps outlined in https://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html # initialization if (!$DateTime) { $DateTime = [DateTime]::UtcNow.ToString("yyyyMMddTHHmmssZ") } Write-Debug "Constructing the CanonicalizedResource Element " $CanonicalizedResource = "" Write-Debug "1. Start with an empty string:`n$CanonicalizedResource" $CanonicalizedResource = $Uri Write-Debug "2. Add the bucketname for virtual host style:`n$EndpointUrl" # only URL encode if service is not S3 if ($Service -ne "s3" -and $Uri -ne "/") { $CanonicalURI = [System.Web.HttpUtility]::UrlEncode($Uri) } else { $CanonicalURI = $Uri } Write-Debug "3. Canonical URI:`n$CanonicalURI" Write-Debug "4. Canonical Query String:`n$CanonicalQueryString" $CanonicalRequest = "$HTTPRequestMethod`n$EndpointUrl`n$CanonicalURI`n$CanonicalQueryString`n" Write-Debug "5. Canonical Request:`n$CanonicalRequest" Write-Debug "Task 2: Calculate the Signature" $hmacsha = New-Object System.Security.Cryptography.HMACSHA256 $hmacsha.Key = [Text.Encoding]::UTF8.GetBytes($SecretAccessKey) $hasher = [System.Security.Cryptography.SHA256]::Create() $SortedHeaders = New-Object 'System.Collections.Generic.SortedDictionary[string, string]' $SortedHeaders["Host"] = $EndpointUrl foreach ($Key in $Headers.Keys) { $SortedHeaders[$Key]=$Headers[$Key] } $StringToSign = "$HTTPRequestMethod`n$ContentMD5`n$ContentType`n$Date`n$SignedHeaders`n$RequestPayloadHash" Write-Debug "StringToSign: $StringToSign" Write-Debug "CanonicalURI: $CanonicalURI" $CanonicalQueryString = "" if ($Query.Keys) { # using Sorted Dictionary as query need to be sorted by encoded keys $SortedEncodedQuery = New-Object 'System.Collections.Generic.SortedDictionary[string, string]' foreach ($Key in $Query.Keys) { # Key and value need to be URL encoded separately $SortedEncodedQuery[[System.Web.HttpUtility]::UrlEncode($Key)]=[System.Web.HttpUtility]::UrlEncode($Query[$Key]) } foreach ($Key in $SortedEncodedQuery.Keys) { $CanonicalQueryString += "$Key=$($SortedEncodedQuery[$Key])&" } $CanonicalQueryString = $CanonicalQueryString -replace "&`$","" } else { $CanonicalQueryString = "" } Write-Debug "CanonicalQueryString: $CanonicalQueryString" Write-Debug "DateTime: $DateTime" Write-Debug "DateString: $DateString" $RequestPayloadHash = ([BitConverter]::ToString($hasher.ComputeHash([Text.Encoding]::UTF8.GetBytes($RequestPayload))) -replace '-','').ToLower() Write-Debug "RequestPayloadHash: $RequestPayloadHash" $CanonicalHeaders = "host:$EndpointUrl`nx-amz-content-sha256:$RequestPayloadHash`nx-amz-date:$DateTime`n" Write-Debug "CanonicalHeaders: $CanonicalHeaders" $SignedHeaders = "host;x-amz-content-sha256;x-amz-date" Write-Debug "SignedHeaders: $SignedHeaders" $CanonicalRequest = "$HTTPRequestMethod`n$CanonicalURI`n$CanonicalQueryString`n$CanonicalHeaders`n$SignedHeaders`n$RequestPayloadHash" Write-Debug "CanonicalRequest: $CanonicalRequest" $CanonicalRequestHash = ([BitConverter]::ToString($hasher.ComputeHash([Text.Encoding]::UTF8.GetBytes($CanonicalRequest))) -replace '-','').ToLower() Write-Debug "CanonicalRequestHash: $CanonicalRequestHash" $StringToSign = "AWS4-HMAC-SHA256`n$DateTime`n$DateString/$Region/$Service/aws4_request`n$CanonicalRequestHash" Write-Debug "StringToSign" $SignatureKey = GetSignatureKey $SecretAccessKey $DateString $Region $Service Write-Debug "SignatureKey: $SignatureKey" $Signature = ([BitConverter]::ToString((sign $SignatureKey $StringToSign)) -replace '-','').ToLower() Write-Output $Signature } } <# .SYNOPSIS Create AWS Authentication Signature Version 4 for Request .DESCRIPTION Create AWS Authentication Signature Version 4 for Request #> function Global:New-AwsSignatureV4 { [CmdletBinding()] PARAM ( [parameter( Mandatory=$True, Position=0, HelpMessage="S3 Access Key")][String]$AccessKey, [parameter( Mandatory=$True, Position=1, HelpMessage="S3 Secret Access Key")][String]$SecretAccessKey, [parameter( Mandatory=$True, Position=2, HelpMessage="EndpointUrl")][String]$EndpointUrl, [parameter( Mandatory=$False, Position=3, HelpMessage="HTTP Request Method")][ValidateSet("OPTIONS","GET","HEAD","PUT","DELETE","TRACE","CONNECT")][String]$HTTPRequestMethod="GET", [parameter( Mandatory=$False, Position=4, HelpMessage="URI")][String]$Uri="/", [parameter( Mandatory=$False, Position=5, HelpMessage="Canonical Query String")][String]$CanonicalQueryString, [parameter( Mandatory=$False, Position=6, HelpMessage="Date Time (yyyyMMddTHHmmssZ)")][String]$DateTime, [parameter( Mandatory=$False, Position=7, HelpMessage="Date String (yyyyMMdd)")][String]$DateString, [parameter( Mandatory=$False, Position=8, HelpMessage="Request payload hash")][String]$RequestPayloadHash, [parameter( Mandatory=$False, Position=9, HelpMessage="Region")][String]$Region="us-east-1", [parameter( Mandatory=$False, Position=10, HelpMessage="Region")][String]$Service="s3", [parameter( Mandatory=$False, Position=11, HelpMessage="Headers")][Hashtable]$Headers=@{}, [parameter( Mandatory=$False, Position=12, HelpMessage="Content type")][String]$ContentType ) Process { # this Cmdlet follows the steps outlined in http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html # initialization if (!$RequestPayloadHash) { $RequestPayloadHash = Get-AwsHash -StringToHash "" } if (!$DateTime) { $DateTime = [DateTime]::UtcNow.ToString("yyyyMMddTHHmmssZ") } if (!$DateString) { $DateString = [DateTime]::UtcNow.ToString('yyyyMMdd') } Write-Debug "Task 1: Create a Canonical Request for Signature Version 4" # http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html Write-Debug "1. HTTP Request Method:`n$HTTPRequestMethod" # only URL encode if service is not S3 if ($Service -ne "s3" -and $Uri -ne "/") { $CanonicalURI = [System.Web.HttpUtility]::UrlEncode($Uri) } else { $CanonicalURI = $Uri } Write-Debug "2. Canonical URI:`n$CanonicalURI" Write-Debug "3. Canonical query string:`n$CanonicalQueryString" if (!$Headers["host"]) { $Headers["host"] = $EndpointUrl } if (!$Headers["x-amz-date"]) { $Headers["x-amz-date"] = $DateTime } if (!$Headers["content-type"] -and $ContentType) { $Headers["content-type"] = $ContentType } $SortedHeaders = ConvertTo-SortedDictionary $Headers $CanonicalHeaders = (($SortedHeaders.GetEnumerator() | % { "$($_.Key):$($_.Value)" }) -join "`n") + "`n" Write-Debug "4. Canonical headers:`n$CanonicalHeaders" $SignedHeaders = $SortedHeaders.Keys -join ";" Write-Debug "5. Signed headers:`n$SignedHeaders" Write-Debug "6. Hashed Payload`n$RequestPayloadHash" $CanonicalRequest = "$HTTPRequestMethod`n$CanonicalURI`n$CanonicalQueryString`n$CanonicalHeaders`n$SignedHeaders`n$RequestPayloadHash" Write-Debug "7. CanonicalRequest:`n$CanonicalRequest" $hasher = [System.Security.Cryptography.SHA256]::Create() $CanonicalRequestHash = ([BitConverter]::ToString($hasher.ComputeHash([Text.Encoding]::UTF8.GetBytes($CanonicalRequest))) -replace '-','').ToLower() Write-Debug "8. Canonical request hash:`n$CanonicalRequestHash" Write-Debug "Task 2: Create a String to Sign for Signature Version 4" # http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html $AlgorithmDesignation = "AWS4-HMAC-SHA256" Write-Debug "1. Algorithm designation:`n$AlgorithmDesignation" Write-Debug "2. request date value, specified with ISO8601 basic format in the format YYYYMMDD'T'HHMMSS'Z:`n$DateTime" $CredentialScope = "$DateString/$Region/$Service/aws4_request" Write-Debug "3. Credential scope:`n$CredentialScope" Write-Debug "4. Canonical request hash:`n$CanonicalRequestHash" $StringToSign = "$AlgorithmDesignation`n$DateTime`n$CredentialScope`n$CanonicalRequestHash" Write-Debug "StringToSign:`n$StringToSign" Write-Debug "Task 3: Calculate the Signature for AWS Signature Version 4" # http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html $SigningKey = GetSignatureKey $SecretAccessKey $DateString $Region $Service Write-Debug "1. Signing Key:`n$([System.BitConverter]::ToString($SigningKey))" $Signature = ([BitConverter]::ToString((sign $SigningKey $StringToSign)) -replace '-','').ToLower() Write-Debug "2. Signature:`n$Signature" Write-Output $Signature } } <# .SYNOPSIS Invoke AWS Request .DESCRIPTION Invoke AWS Request #> function Global:Invoke-AwsRequest { [CmdletBinding()] PARAM ( [parameter( ParameterSetName="profile", Mandatory=$True, Position=0, HelpMessage="AWS Profile to use which contains AWS sredentials and settings")][String]$Profile, [parameter( ParameterSetName="keys", Mandatory=$False, Position=0, HelpMessage="S3 Access Key")][String]$AccessKey, [parameter( ParameterSetName="keys", Mandatory=$False, Position=1, HelpMessage="S3 Secret Access Key")][String]$SecretAccessKey, [parameter( Mandatory=$False, Position=2, HelpMessage="HTTP Request Method")][ValidateSet("OPTIONS","GET","HEAD","PUT","DELETE","TRACE","CONNECT")][String]$HTTPRequestMethod="GET", [parameter( Mandatory=$False, Position=3, HelpMessage="EndpointUrl")][String]$EndpointUrl, [parameter( Mandatory=$False, Position=4, HelpMessage="Skip SSL Certificate check")][Switch]$SkipCertificateCheck, [parameter( Mandatory=$False, Position=5, HelpMessage="URI")][String]$Uri="/", [parameter( Mandatory=$False, Position=6, HelpMessage="Force HTTP")][Switch]$HTTP, [parameter( Mandatory=$False, Position=7, HelpMessage="Query String")][Hashtable]$Query, [parameter( Mandatory=$False, Position=8, HelpMessage="Request payload")][String]$RequestPayload="", [parameter( Mandatory=$False, Position=9, HelpMessage="Region")][String]$Region="us-east-1", [parameter( Mandatory=$False, Position=9, HelpMessage="Region")][String]$Service="s3", [parameter( Mandatory=$False, Position=10, HelpMessage="AWS Signer type (S3 for V2 Authentication and AWS4 for V4 Authentication)")][String][ValidateSet("S3","AWS4")]$SingerType="AWS4", [parameter( Mandatory=$False, Position=11, HelpMessage="Headers")][Hashtable]$Headers=@{}, [parameter( Mandatory=$False, Position=12, HelpMessage="Content type")][String]$ContentType, [parameter( Mandatory=$False, Position=13, HelpMessage="Path where output should be saved to")][String]$FilePath, [parameter( Mandatory=$False, Position=14, HelpMessage="Bucket name")][String]$Bucket, [parameter( Mandatory=$False, Position=15, HelpMessage="Tenant name")][String]$Tenant, [parameter( Mandatory=$False, Position=16, HelpMessage="Use virtual host style for V2 Authentication")][Switch]$VirtualHostStyle ) Begin { $Credential = $null # convenience method to autogenerate credentials if ($CurrentSGWServer) { if (!$Profile -and !$AccessKey -and $CurrentSGWServer.AccountId -and $EndpointUrl) { Write-Warning "No profile and no access key specified, but connected to a StorageGRID tenant. Therefore autogenerating temporary AWS credentials and removing them after command execution" $Credential = New-SGWS3AccessKey -Expires (Get-Date).AddMinutes(5) } elseif (!$Profile -and !$AccessKey -and $Tenant -and $CurrentSGWServer.SupportedApiVersions.Contains(1)) { Write-Warning "No profile and no access key specified, but connected to a StorageGRID server. Therefore autogenerating temporary AWS credentials for tenant $Tenant and removing them after command execution" $Credential = Get-SGWAccount -Name $Tenant | New-SGWS3AccessKey -Expires (Get-Date).AddMinutes(5) } elseif (!$Profile -and !$AccessKey -and $Bucket -and $CurrentSGWServer.SupportedApiVersions.Contains(1)) { # need to check each account for its buckets to determine which account the bucket belongs to $AccountId = foreach ($Account in (Get-SgwAccounts)) { if ($Account | Get-SGWAccountUsage | select -ExpandProperty buckets | ? { $_.name -eq $Bucket }) { Write-Output $Account.id break } } if ($AccountId) { Write-Warning "No profile and no access key specified, therefore autogenerating temporary AWS credentials and removing them after command execution" $Credential = New-SGWS3AccessKey -AccountId $AccountId -Expires (Get-Date).AddMinutes(5) } else { $Profile = "default" } } else { $Profile = "default" } if ($Credential -and !$EndpointUrl -and !$CurrentSGWServer.accountId) { Write-Verbose "EndpointUrl not specified, but connected to StorageGRID server. Trying to connect with any of the endpoint domain names specified in the grid" $EndpointDomainNames = Get-SGWEndpointDomainNames $EndpointUrl = foreach ($EndpointDomainName in $EndpointDomainNames) { try { $Null = Get-S3Buckets -AccessKey $Credential.accessKey -SecretAccessKey $Credential.secretAccessKey -EndpointUrl $EndpointDomainName Write-Output $EndpointDomainName break } catch { } } } if ($Credential -and $EndpointUrl) { $CleanupCredential = $true $AccessKey = $Credential.accessKey $SecretAccessKey = $Credential.secretAccessKey } elseif ($Credential) { $Credential | Remove-SGWS3AccessKey $Profile = "default" } } if ($Profile -and !$AccessKey) { Write-Verbose "Using credentials from profile $Profile" if (!(Test-Path $AWS_CREDENTIALS_FILE)) { throw "Profile $Profile does not contain credentials. Either connect to a StorageGRID Server using Connect-SgwServer or add credentials to the default profile with Add-AwsCredentials" } $Credentials = ConvertFrom-AwsConfigFile -AwsConfigFile $AWS_CREDENTIALS_FILE $AccessKey = $Credentials[$Profile].aws_access_key_id $SecretAccessKey = $Credentials[$Profile].aws_secret_access_key if (Test-Path $AWS_CONFIG_FILE) { $Config = ConvertFrom-AwsConfigFile -AwsConfigFile $AWS_CONFIG_FILE if (!$Region) { $Region = $Config[$Profile].region } } } if (!$AccessKey) { throw "No Access Key specified" } if (!$SecretAccessKey) { throw "No Secret Access Key specified" } # check if untrusted SSL certificates should be ignored if ($SkipCertificateCheck) { if ($PSVersionTable.PSVersion.Major -lt 6) { [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy } else { if (!"Invoke-RestMethod:SkipCertificateCheck") { $PSDefaultParameterValues.Add("Invoke-RestMethod:SkipCertificateCheck",$true) } else { $PSDefaultParameterValues.'Invoke-RestMethod:SkipCertificateCheck'=$true } } } else { # currently there is no way to re-enable certificate check for the current session in PowerShell prior to version 6 if ($PSVersionTable.PSVersion.Major -ge 6) { if ("Invoke-RestMethod:SkipCertificateCheck") { $PSDefaultParameterValues.Remove("Invoke-RestMethod:SkipCertificateCheck") } } } if ([environment]::OSVersion.Platform -match "Win") { # check if proxy is used $ProxyRegistry = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" $ProxySettings = Get-ItemProperty -Path $ProxyRegistry if ($ProxySettings.ProxyEnable) { Write-Warning "Proxy Server $($ProxySettings.ProxyServer) configured in Internet Explorer may be used to connect to the endpoint!" } if ($ProxySettings.AutoConfigURL) { Write-Warning "Proxy Server defined in automatic proxy configuration script $($ProxySettings.AutoConfigURL) configured in Internet Explorer may be used to connect to the endpoint!" } } if (!$EndpointUrl) { if ($Region -eq "us-east-1") { $EndpointUrl = "s3.amazonaws.com" } else { $EndpointUrl = "s3.$Region.amazonaws.com" } } # remove port 80 and port 443 from EndpointUrl as they must not be included $EndpointUrl -replace ':80$','' -replace ':443$','' } Process { $DateTime = [DateTime]::UtcNow.ToString("yyyyMMddTHHmmssZ") $DateString = [DateTime]::UtcNow.ToString('yyyyMMdd') $CanonicalQueryString = "" if ($Query.Keys.Count -ge 1) { # using Sorted Dictionary as query need to be sorted by encoded keys $SortedEncodedQuery = New-Object 'System.Collections.Generic.SortedDictionary[string, string]' foreach ($Key in $Query.Keys) { # Key and value need to be URL encoded separately $SortedEncodedQuery[[System.Web.HttpUtility]::UrlEncode($Key)]=[System.Web.HttpUtility]::UrlEncode($Query[$Key]) } foreach ($Key in $SortedEncodedQuery.Keys) { $CanonicalQueryString += "$Key=$($SortedEncodedQuery[$Key])&" } $CanonicalQueryString = $CanonicalQueryString -replace "&`$","" } $RequestPayloadHash=Get-AWSHash -StringToHash $RequestPayload if (!$Headers["host"]) { $Headers["host"] = $EndpointUrl } if (!$Headers["x-amz-content-sha256"]) { $Headers["x-amz-content-sha256"] = $RequestPayloadHash } if (!$Headers["x-amz-date"]) { $Headers["x-amz-date"] = $DateTime } if (!$Headers["content-type"] -and $ContentType) { $Headers["content-type"] = $ContentType } $SortedHeaders = ConvertTo-SortedDictionary $Headers $SignedHeaders = $SortedHeaders.Keys -join ";" if ($SingerType = "AWS4") { Write-Verbose "Using AWS Signature Version 4" $Signature = New-AwsSignatureV4 -AccessKey $AccessKey -SecretAccessKey $SecretAccessKey -EndpointUrl $EndpointUrl -Uri $Uri -CanonicalQueryString $CanonicalQueryString -HTTPRequestMethod $HTTPRequestMethod -RequestPayloadHash $RequestPayloadHash -DateTime $DateTime -DateString $DateString -Headers $Headers Write-Debug "Task 4: Add the Signing Information to the Request" # http://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html $Headers["Authorization"]="AWS4-HMAC-SHA256 Credential=$AccessKey/$DateString/$Region/$Service/aws4_request,SignedHeaders=$SignedHeaders,Signature=$Signature" Write-Debug "Headers:`n$(ConvertTo-Json -InputObject $Headers)" } else { Write-Verbose "Using AWS Signature Version 2" $Signature = New-AwsSignatureV2 -AccessKey $AccessKey -SecretAccessKey $SecretAccessKey -EndpointUrl $EndpointUrl -Uri $Uri -HTTPRequestMethod $HTTPRequestMethod -ContentMD5 $ContentMd5 -ContentType $ContentType -Date $DateTime -Bucket $Bucket -VirtualHostStyle:$VirtualHostStyle } if ($HTTP) { $Protocol = "http://" } else { $Protocol = "https://" } if ($CanonicalQueryString) { $Url = $Protocol + $EndpointUrl + $Uri + "?" + $CanonicalQueryString } else { $Url = $Protocol + $EndpointUrl + $Uri } try { #$Result = Invoke-RestMethod -Method $HTTPRequestMethod -Uri $Url -Headers $Headers -OutFile $FilePath $Result = Invoke-RestMethod -Method $HTTPRequestMethod -Uri $Url -Headers $Headers } catch { $ResponseBody = ParseExceptionBody $_.Exception.Response Write-Error "$HTTPRequestMethod to $Url failed with Exception $($_.Exception.Message) `n $responseBody" } Write-Output $Result } End { if ($CleanupCredential) { $Credential | Remove-SGWS3AccessKey } } } <# .SYNOPSIS Add AWS Credentials .DESCRIPTION Add AWS Credentials #> function Global:Add-AwsCredentials { [CmdletBinding(DefaultParameterSetName="credential")] PARAM ( [parameter( Mandatory=$False, Position=0, HelpMessage="AWS Profile to use which contains AWS sredentials and settings")][String]$Profile="default", [parameter( ParameterSetName="credential", Mandatory=$True, Position=1, HelpMessage="Credential")][PSCredential]$Credential, [parameter( ParameterSetName="keys", Mandatory=$True, Position=1, HelpMessage="S3 Access Key")][String]$AccessKey, [parameter( ParameterSetName="keys", Mandatory=$True, Position=2, HelpMessage="S3 Secret Access Key")][String]$SecretAccessKey, [parameter( Mandatory=$False, Position=3, HelpMessage="Custom endpoint URL if different then AWS URL")][String]$EndpointUrl ) Process { $Credentials = ConvertFrom-AwsConfigFile -AwsConfigFile $AWS_CREDENTIALS_FILE $Credentials[$Profile] = @{} $Credentials[$Profile]["aws_access_key_id"] = $AccessKey $Credentials[$Profile]["aws_secret_access_key"] = $SecretAccessKey } } ### S3 Cmdlets ### <# .SYNOPSIS Get S3 Buckets .DESCRIPTION Get S3 Buckets #> function Global:Get-S3Buckets { [CmdletBinding(DefaultParameterSetName="none")] PARAM ( [parameter( Mandatory=$False, Position=0, HelpMessage="EndpointUrl")][String]$EndpointUrl, [parameter( Mandatory=$False, Position=1, HelpMessage="Skip SSL Certificate Check")][Switch]$SkipCertificateCheck, [parameter( ParameterSetName="profile", Mandatory=$False, Position=2, HelpMessage="AWS Profile to use which contains AWS credentials and settings")][String]$Profile, [parameter( ParameterSetName="keys", Mandatory=$False, Position=2, HelpMessage="S3 Access Key")][String]$AccessKey, [parameter( ParameterSetName="keys", Mandatory=$False, Position=3, HelpMessage="S3 Secret Access Key")][String]$SecretAccessKey, [parameter( ParameterSetName="tenant", Mandatory=$False, Position=2, HelpMessage="StorageGRID tenant name to execute this command against")][String]$Tenant ) Process { $Uri = '/' $HTTPRequestMethod = "GET" if ($Profile) { $Result = Invoke-AwsRequest -Profile $Profile -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -ErrorAction Stop } elseif ($AccessKey) { $Result = Invoke-AwsRequest -AccessKey $AccessKey -SecretAccessKey $SecretAccessKey -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -ErrorAction Stop } elseif ($Tenant) { $Result = Invoke-AwsRequest -Tenant $Tenant -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -ErrorAction Stop } else { $Result = Invoke-AwsRequest -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -ErrorAction Stop } $Buckets = @() if ($Result.ListAllMyBucketsResult) { foreach ($Bucket in $Result.ListAllMyBucketsResult.Buckets.ChildNodes) { $Buckets += [PSCustomObject]@{Name=$Bucket.Name;CreationDate=$Bucket.CreationDate} } $Owner = [PSCustomObject]@{ID=$Result.ListAllMyBucketsResult.Owner.ID;DisplayName=$Result.ListAllMyBucketsResult.Owner.DisplayName} } $Buckets | Add-Member -MemberType NoteProperty -Name OwnerId -Value $Owner.ID $Buckets | Add-Member -MemberType NoteProperty -Name OwnerDisplayName -Value $Owner.DisplayName Write-Output $Buckets } } <# .SYNOPSIS Get S3 Bucket .DESCRIPTION Get S3 Bucket #> function Global:Get-S3Bucket { [CmdletBinding(DefaultParameterSetName="none")] PARAM ( [parameter( ParameterSetName="profile", Mandatory=$False, Position=0, HelpMessage="AWS Profile to use which contains AWS sredentials and settings")][String]$Profile, [parameter( ParameterSetName="credential", Mandatory=$False, Position=0, HelpMessage="Credential")][PSCredential]$Credential, [parameter( ParameterSetName="keys", Mandatory=$False, Position=0, HelpMessage="S3 Access Key")][String]$AccessKey, [parameter( ParameterSetName="keys", Mandatory=$False, Position=1, HelpMessage="S3 Secret Access Key")][String]$SecretAccessKey, [parameter( ParameterSetName="tenant", Mandatory=$False, Position=0, HelpMessage="StorageGRID tenant name to execute this command against")][String]$Tenant, [parameter( Mandatory=$False, Position=2, HelpMessage="EndpointUrl")][String]$EndpointUrl, [parameter( Mandatory=$False, Position=3, HelpMessage="Skip SSL Certificate Check")][Switch]$SkipCertificateCheck, [parameter( Mandatory=$False, Position=4, HelpMessage="Path Style")][String][ValidateSet("path","virtual-hosted")]$UrlStyle="path", [parameter( Mandatory=$True, Position=5, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, HelpMessage="Bucket")][Alias("Name")][String]$Bucket, [parameter( Mandatory=$False, Position=6, HelpMessage="Maximum Number of keys to return")][Int][ValidateRange(0,1000)]$MaxKeys=0, [parameter( Mandatory=$False, Position=7, HelpMessage="Bucket prefix for filtering")][String]$Prefix, [parameter( Mandatory=$False, Position=8, HelpMessage="Bucket prefix for filtering")][String][ValidateLength(1,1)]$Delimiter, [parameter( Mandatory=$False, Position=9, HelpMessage="Return Owner information (Only valid for list type 2).")][Switch]$FetchOwner=$False, [parameter( Mandatory=$False, Position=10, HelpMessage="Return key names after a specific object key in your key space. The S3 service lists objects in UTF-8 character encoding in lexicographical order (Only valid for list type 2).")][String]$StartAfter, [parameter( Mandatory=$False, Position=11, HelpMessage="Continuation token (Only valid for list type 1).")][String]$Marker, [parameter( Mandatory=$False, Position=12, HelpMessage="Continuation token (Only valid for list type 2).")][String]$ContinuationToken, [parameter( Mandatory=$False, Position=13, HelpMessage="Encoding type (Only allowed value is url).")][String][ValidateSet("url")]$EncodingType, [parameter( Mandatory=$False, Position=14, HelpMessage="Bucket list type.")][String][ValidateSet(1,2)]$ListType=1 ) Process { if ($UrlStyle -eq "virtual-hosted") { Write-Verbose "Using virtual-hosted style URL" $Uri = "/" $EndpointUrl = $Bucket + "." + $EndpointUrl } else { Write-Verbose "Using path style URL" $Uri = "/$Bucket/" } $HTTPRequestMethod = "GET" $Query = @{} if ($Delimiter) { $Query["delimiter"] = $Delimiter } if ($EncodingType) { $Query["encoding-type"] = $EncodingType } if ($MaxKeys -ge 1) { $Query["max-keys"] = $MaxKeys } if ($Prefix) { $Query["prefix"] = $Prefix } # S3 supports two types for listing buckets, but only v2 is recommended, thus using list-type=2 query parameter if ($ListType -eq 1) { if ($Marker) { $Query["marker"] = $Marker } } else { $Query["list-type"] = 2 if ($FetchOwner) { $Query["fetch-owner"] = $FetchOwner } if ($StartAfter) { $Query["start-after"] = $StartAfter } if ($ContinuationToken) { $Query["continuation-token"] = $ContinuationToken } } if ($Profile) { $Result = Invoke-AwsRequest -Profile $Profile -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } elseif ($AccessKey) { $Result = Invoke-AwsRequest -AccessKey $AccessKey -SecretAccessKey $SecretAccessKey -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } elseif ($Tenant) { $Result = Invoke-AwsRequest -Tenant $Tenant -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } else { $Result = Invoke-AwsRequest -Bucket $Bucket -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } $Objects = $Result.ListBucketResult.Contents | ? { $_ } $Objects | Add-Member -MemberType NoteProperty -Name Bucket -Value $Result.ListBucketResult.Name if ($Result.ListBucketResult.IsTruncated -eq "true" -and $MaxKeys -eq 0) { Write-Verbose "1000 Objects were returned and max keys was not limited so continuing to get all objects" Write-Debug "NextMarker: $($Result.ListBucketResult.NextMarker)" if ($Profile) { $Objects += Get-S3Bucket -Profile $Profile -EndpointUrl $EndpointUrl -SkipCertificateCheck:$SkipCertificateCheck -UrlStyle $UrlStyle -Bucket $Bucket -MaxKeys $MaxKeys -Prefix $Prefix -FetchOwner:$FetchOwner -StartAfter $StartAfter -ContinuationToken $Result.ListBucketResult.NextContinuationToken -Marker $Result.ListBucketResult.NextMarker } elseif ($Credential) { $Objects += Get-S3Bucket -Credential $Credential -EndpointUrl $EndpointUrl -SkipCertificateCheck:$SkipCertificateCheck -UrlStyle $UrlStyle -Bucket $Bucket -MaxKeys $MaxKeys -Prefix $Prefix -FetchOwner:$FetchOwner -StartAfter $StartAfter -ContinuationToken $Result.ListBucketResult.NextContinuationToken -Marker $Result.ListBucketResult.NextMarker } else { $Objects += Get-S3Bucket -AccessKey $AccessKey -SecretAccessKey $SecretAccessKey -EndpointUrl $EndpointUrl -SkipCertificateCheck:$SkipCertificateCheck -UrlStyle $UrlStyle -Bucket $Bucket -MaxKeys $MaxKeys -Prefix $Prefix -FetchOwner:$FetchOwner -StartAfter $StartAfter -ContinuationToken $Result.ListBucketResult.NextContinuationToken -Marker $Result.ListBucketResult.NextMarker } } Write-Output $Objects } } <# .SYNOPSIS Get S3 Bucket Consistency Setting .DESCRIPTION Get S3 Bucket Consistency Setting #> function Global:Get-S3BucketConsistency { [CmdletBinding(DefaultParameterSetName="none")] PARAM ( [parameter( ParameterSetName="profile", Mandatory=$False, Position=0, HelpMessage="AWS Profile to use which contains AWS sredentials and settings")][String]$Profile, [parameter( ParameterSetName="keys", Mandatory=$False, Position=0, HelpMessage="S3 Access Key")][String]$AccessKey, [parameter( ParameterSetName="keys", Mandatory=$False, Position=1, HelpMessage="S3 Secret Access Key")][String]$SecretAccessKey, [parameter( ParameterSetName="tenant", Mandatory=$False, Position=0, HelpMessage="StorageGRID tenant name to execute this command against")][String]$Tenant, [parameter( Mandatory=$False, Position=2, HelpMessage="EndpointUrl")][String]$EndpointUrl, [parameter( Mandatory=$False, Position=3, HelpMessage="Skip SSL Certificate Check")][Switch]$SkipCertificateCheck, [parameter( Mandatory=$False, Position=4, HelpMessage="Path Style")][String][ValidateSet("path","virtual-hosted")]$UrlStyle="path", [parameter( Mandatory=$True, Position=5, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, HelpMessage="Bucket")][Alias("Name")][String]$Bucket ) Process { if ($UrlStyle -eq "virtual-hosted") { Write-Verbose "Using virtual-hosted style URL" $Uri = "/" $EndpointUrl = $Bucket + "." + $EndpointUrl } else { Write-Verbose "Using path style URL" $Uri = "/$Bucket/" } $HTTPRequestMethod = "GET" $Query = @{"x-ntap-sg-consistency"=""} if ($Profile) { $Result = Invoke-AwsRequest -Profile $Profile -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } elseif ($AccessKey) { $Result = Invoke-AwsRequest -AccessKey $AccessKey -SecretAccessKey $SecretAccessKey -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } elseif ($Tenant) { $Result = Invoke-AwsRequest -Tenant $Tenant -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } else { $Result = Invoke-AwsRequest -Bucket $Bucket -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } $BucketConsistency = [PSCustomObject]@{Bucket=$Bucket;Consistency=$Result.Consistency.InnerText} Write-Output $BucketConsistency } } <# .SYNOPSIS Modify S3 Bucket Consistency Setting .DESCRIPTION Modify S3 Bucket Consistency Setting #> function Global:Update-S3BucketConsistency { [CmdletBinding(DefaultParameterSetName="none")] PARAM ( [parameter( ParameterSetName="profile", Mandatory=$False, Position=0, HelpMessage="AWS Profile to use which contains AWS sredentials and settings")][String]$Profile, [parameter( ParameterSetName="keys", Mandatory=$False, Position=0, HelpMessage="S3 Access Key")][String]$AccessKey, [parameter( ParameterSetName="keys", Mandatory=$False, Position=1, HelpMessage="S3 Secret Access Key")][String]$SecretAccessKey, [parameter( ParameterSetName="tenant", Mandatory=$False, Position=0, HelpMessage="StorageGRID tenant name to execute this command against")][String]$Tenant, [parameter( Mandatory=$False, Position=2, HelpMessage="EndpointUrl")][String]$EndpointUrl, [parameter( Mandatory=$False, Position=3, HelpMessage="Skip SSL Certificate Check")][Switch]$SkipCertificateCheck, [parameter( Mandatory=$False, Position=4, HelpMessage="Path Style")][String][ValidateSet("path","virtual-hosted")]$UrlStyle="path", [parameter( Mandatory=$True, Position=5, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, HelpMessage="Bucket")][Alias("Name")][String]$Bucket, [parameter( Mandatory=$True, Position=5, HelpMessage="Bucket")][ValidateSet("all","strong-global","strong-site","default","available","weak")][String]$Consistency ) Process { if ($UrlStyle -eq "virtual-hosted") { Write-Verbose "Using virtual-hosted style URL" $Uri = "/" $EndpointUrl = $Bucket + "." + $EndpointUrl } else { Write-Verbose "Using path style URL" $Uri = "/$Bucket/" } $HTTPRequestMethod = "PUT" $Query = @{"x-ntap-sg-consistency"=$Consistency} if ($Profile) { $Result = Invoke-AwsRequest -Profile $Profile -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } elseif ($AccessKey) { $Result = Invoke-AwsRequest -AccessKey $AccessKey -SecretAccessKey $SecretAccessKey -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } elseif ($Tenant) { $Result = Invoke-AwsRequest -Tenant $Tenant -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } else { $Result = Invoke-AwsRequest -Bucket $Bucket -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } } } <# .SYNOPSIS Get S3 Bucket Storage Usage .DESCRIPTION Get S3 Bucket Storage Usage #> function Global:Get-S3StorageUsage { [CmdletBinding(DefaultParameterSetName="none")] PARAM ( [parameter( ParameterSetName="profile", Mandatory=$False, Position=0, HelpMessage="AWS Profile to use which contains AWS sredentials and settings")][String]$Profile, [parameter( ParameterSetName="keys", Mandatory=$False, Position=0, HelpMessage="S3 Access Key")][String]$AccessKey, [parameter( ParameterSetName="keys", Mandatory=$False, Position=1, HelpMessage="S3 Secret Access Key")][String]$SecretAccessKey, [parameter( ParameterSetName="tenant", Mandatory=$False, Position=0, HelpMessage="StorageGRID tenant name to execute this command against")][String]$Tenant, [parameter( Mandatory=$False, Position=2, HelpMessage="EndpointUrl")][String]$EndpointUrl, [parameter( Mandatory=$False, Position=3, HelpMessage="Skip SSL Certificate Check")][Switch]$SkipCertificateCheck ) Process { $Uri = "/" $HTTPRequestMethod = "GET" $Query = @{"x-ntap-sg-usage"=""} if ($Profile) { $Result = Invoke-AwsRequest -Profile $Profile -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } elseif ($AccessKey) { $Result = Invoke-AwsRequest -AccessKey $AccessKey -SecretAccessKey $SecretAccessKey -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } elseif ($Tenant) { $Result = Invoke-AwsRequest -Tenant $Tenant -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } else { $Result = Invoke-AwsRequest -Bucket $Bucket -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } $UsageResult = [PSCustomObject]@{CalculationTime=(Get-Date -Date $Result.UsageResult.CalculationTime);ObjectCount=$Result.UsageResult.ObjectCount;DataBytes=$Result.UsageResult.DataBytes;buckets=$Result.UsageResult.Buckets.ChildNodes} Write-Output $UsageResult } } <# .SYNOPSIS Get S3 Bucket Last Access Time .DESCRIPTION Get S3 Bucket Last Access Time #> function Global:Get-S3BucketLastAccessTime { [CmdletBinding(DefaultParameterSetName="none")] PARAM ( [parameter( ParameterSetName="profile", Mandatory=$False, Position=0, HelpMessage="AWS Profile to use which contains AWS sredentials and settings")][String]$Profile, [parameter( ParameterSetName="keys", Mandatory=$False, Position=0, HelpMessage="S3 Access Key")][String]$AccessKey, [parameter( ParameterSetName="keys", Mandatory=$False, Position=1, HelpMessage="S3 Secret Access Key")][String]$SecretAccessKey, [parameter( ParameterSetName="tenant", Mandatory=$False, Position=0, HelpMessage="StorageGRID tenant name to execute this command against")][String]$Tenant, [parameter( Mandatory=$False, Position=2, HelpMessage="EndpointUrl")][String]$EndpointUrl, [parameter( Mandatory=$False, Position=3, HelpMessage="Skip SSL Certificate Check")][Switch]$SkipCertificateCheck, [parameter( Mandatory=$False, Position=4, HelpMessage="Path Style")][String][ValidateSet("path","virtual-hosted")]$UrlStyle="path", [parameter( Mandatory=$True, Position=5, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, HelpMessage="Bucket")][Alias("Name")][String]$Bucket ) Process { if ($UrlStyle -eq "virtual-hosted") { Write-Verbose "Using virtual-hosted style URL" $Uri = "/" $EndpointUrl = $Bucket + "." + $EndpointUrl } else { Write-Verbose "Using path style URL" $Uri = "/$Bucket/" } $HTTPRequestMethod = "GET" $Query = @{"x-ntap-sg-lastaccesstime"=""} if ($Profile) { $Result = Invoke-AwsRequest -Profile $Profile -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } elseif ($AccessKey) { $Result = Invoke-AwsRequest -AccessKey $AccessKey -SecretAccessKey $SecretAccessKey -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } elseif ($Tenant) { $Result = Invoke-AwsRequest -Tenant $Tenant -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } else { $Result = Invoke-AwsRequest -Bucket $Bucket -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } $BucketLastAccessTime = [PSCustomObject]@{Bucket=$Bucket;LastAccessTime=$Result.LastAccessTime.InnerText} Write-Output $BucketLastAccessTime } } <# .SYNOPSIS Enable S3 Bucket Last Access Time .DESCRIPTION Enable S3 Bucket Last Access Time #> function Global:Enable-S3BucketLastAccessTime { [CmdletBinding(DefaultParameterSetName="none")] PARAM ( [parameter( ParameterSetName="profile", Mandatory=$False, Position=0, HelpMessage="AWS Profile to use which contains AWS sredentials and settings")][String]$Profile, [parameter( ParameterSetName="keys", Mandatory=$False, Position=0, HelpMessage="S3 Access Key")][String]$AccessKey, [parameter( ParameterSetName="keys", Mandatory=$False, Position=1, HelpMessage="S3 Secret Access Key")][String]$SecretAccessKey, [parameter( ParameterSetName="tenant", Mandatory=$False, Position=0, HelpMessage="StorageGRID tenant name to execute this command against")][String]$Tenant, [parameter( Mandatory=$False, Position=2, HelpMessage="EndpointUrl")][String]$EndpointUrl, [parameter( Mandatory=$False, Position=3, HelpMessage="Skip SSL Certificate Check")][Switch]$SkipCertificateCheck, [parameter( Mandatory=$False, Position=4, HelpMessage="Path Style")][String][ValidateSet("path","virtual-hosted")]$UrlStyle="path", [parameter( Mandatory=$True, Position=5, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, HelpMessage="Bucket")][Alias("Name")][String]$Bucket ) Process { if ($UrlStyle -eq "virtual-hosted") { Write-Verbose "Using virtual-hosted style URL" $Uri = "/" $EndpointUrl = $Bucket + "." + $EndpointUrl } else { Write-Verbose "Using path style URL" $Uri = "/$Bucket/" } $HTTPRequestMethod = "PUT" $Query = @{"x-ntap-sg-lastaccesstime"="enabled"} if ($Profile) { $Result = Invoke-AwsRequest -Profile $Profile -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } elseif ($AccessKey) { $Result = Invoke-AwsRequest -AccessKey $AccessKey -SecretAccessKey $SecretAccessKey -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } elseif ($Tenant) { $Result = Invoke-AwsRequest -Tenant $Tenant -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } else { $Result = Invoke-AwsRequest -Bucket $Bucket -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } } } <# .SYNOPSIS Disable S3 Bucket Last Access Time .DESCRIPTION Disable S3 Bucket Last Access Time #> function Global:Disable-S3BucketLastAccessTime { [CmdletBinding(DefaultParameterSetName="none")] PARAM ( [parameter( ParameterSetName="profile", Mandatory=$False, Position=0, HelpMessage="AWS Profile to use which contains AWS sredentials and settings")][String]$Profile, [parameter( ParameterSetName="keys", Mandatory=$False, Position=0, HelpMessage="S3 Access Key")][String]$AccessKey, [parameter( ParameterSetName="keys", Mandatory=$False, Position=1, HelpMessage="S3 Secret Access Key")][String]$SecretAccessKey, [parameter( ParameterSetName="tenant", Mandatory=$False, Position=0, HelpMessage="StorageGRID tenant name to execute this command against")][String]$Tenant, [parameter( Mandatory=$False, Position=2, HelpMessage="EndpointUrl")][String]$EndpointUrl, [parameter( Mandatory=$False, Position=3, HelpMessage="Skip SSL Certificate Check")][Switch]$SkipCertificateCheck, [parameter( Mandatory=$False, Position=4, HelpMessage="Path Style")][String][ValidateSet("path","virtual-hosted")]$UrlStyle="path", [parameter( Mandatory=$True, Position=5, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, HelpMessage="Bucket")][Alias("Name")][String]$Bucket ) Process { if ($UrlStyle -eq "virtual-hosted") { Write-Verbose "Using virtual-hosted style URL" $Uri = "/" $EndpointUrl = $Bucket + "." + $EndpointUrl } else { Write-Verbose "Using path style URL" $Uri = "/$Bucket/" } $HTTPRequestMethod = "PUT" $Query = @{"x-ntap-sg-lastaccesstime"="disabled"} if ($Profile) { $Result = Invoke-AwsRequest -Profile $Profile -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } elseif ($AccessKey) { $Result = Invoke-AwsRequest -AccessKey $AccessKey -SecretAccessKey $SecretAccessKey -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } elseif ($Tenant) { $Result = Invoke-AwsRequest -Tenant $Tenant -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } else { $Result = Invoke-AwsRequest -Bucket $Bucket -HTTPRequestMethod $HTTPRequestMethod -EndpointUrl $EndpointUrl -Uri $Uri -SkipCertificateCheck:$SkipCertificateCheck -Query $Query -ErrorAction Stop } } } # SIG # Begin signature block # MIIVAAYJKoZIhvcNAQcCoIIU8TCCFO0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUzEoMiZeAxh+hk9OUQLvIuTWJ # gVGggg/vMIIEmTCCA4GgAwIBAgIPFojwOSVeY45pFDkH5jMLMA0GCSqGSIb3DQEB # BQUAMIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQg # TGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNV # BAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEdMBsGA1UEAxMUVVROLVVTRVJG # aXJzdC1PYmplY3QwHhcNMTUxMjMxMDAwMDAwWhcNMTkwNzA5MTg0MDM2WjCBhDEL # MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE # BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKjAoBgNVBAMT # IUNPTU9ETyBTSEEtMSBUaW1lIFN0YW1waW5nIFNpZ25lcjCCASIwDQYJKoZIhvcN # AQEBBQADggEPADCCAQoCggEBAOnpPd/XNwjJHjiyUlNCbSLxscQGBGue/YJ0UEN9 # xqC7H075AnEmse9D2IOMSPznD5d6muuc3qajDjscRBh1jnilF2n+SRik4rtcTv6O # KlR6UPDV9syR55l51955lNeWM/4Og74iv2MWLKPdKBuvPavql9LxvwQQ5z1IRf0f # aGXBf1mZacAiMQxibqdcZQEhsGPEIhgn7ub80gA9Ry6ouIZWXQTcExclbhzfRA8V # zbfbpVd2Qm8AaIKZ0uPB3vCLlFdM7AiQIiHOIiuYDELmQpOUmJPv/QbZP7xbm1Q8 # ILHuatZHesWrgOkwmt7xpD9VTQoJNIp1KdJprZcPUL/4ygkCAwEAAaOB9DCB8TAf # BgNVHSMEGDAWgBTa7WR0FJwUPKvdmam9WyhNizzJ2DAdBgNVHQ4EFgQUjmstM2v0 # M6eTsxOapeAK9xI1aogwDgYDVR0PAQH/BAQDAgbAMAwGA1UdEwEB/wQCMAAwFgYD # VR0lAQH/BAwwCgYIKwYBBQUHAwgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2Ny # bC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0LmNybDA1BggrBgEF # BQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20w # DQYJKoZIhvcNAQEFBQADggEBALozJEBAjHzbWJ+zYJiy9cAx/usfblD2CuDk5oGt # Joei3/2z2vRz8wD7KRuJGxU+22tSkyvErDmB1zxnV5o5NuAoCJrjOU+biQl/e8Vh # f1mJMiUKaq4aPvCiJ6i2w7iH9xYESEE9XNjsn00gMQTZZaHtzWkHUxY93TYCCojr # QOUGMAu4Fkvc77xVCf/GPhIudrPczkLv+XZX4bcKBUCYWJpdcRaTcYxlgepv84n3 # +3OttOe/2Y5vqgtPJfO44dXddZhogfiqwNGAwsTEOYnB9smebNd0+dmX+E/CmgrN # Xo/4GengpZ/E8JIh5i15Jcki+cPwOoRXrToW9GOUEB1d0MYwggVqMIIEUqADAgEC # AhEAsPVV6jnRNgfABcUA7um0CDANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJH # QjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3Jk # MRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEjMCEGA1UEAxMaQ09NT0RPIFJT # QSBDb2RlIFNpZ25pbmcgQ0EwHhcNMTcwNjI4MDAwMDAwWhcNMjAwNjI3MjM1OTU5 # WjCBqzELMAkGA1UEBhMCREUxDjAMBgNVBBEMBTg1NTUxMRwwGgYDVQQIDBNOb3Jk # cmhlaW4tV2VzdGZhbGVuMRIwEAYDVQQHDAlLaXJjaGhlaW0xFjAUBgNVBAkMDVNv # bm5lbmFsbGVlIDExIDAeBgNVBAoMF05ldEFwcCBEZXV0c2NobGFuZCBHbWJIMSAw # HgYDVQQDDBdOZXRBcHAgRGV1dHNjaGxhbmQgR21iSDCCASIwDQYJKoZIhvcNAQEB # BQADggEPADCCAQoCggEBAKnyWe0IrmZ8r6hU9qhoH7pNv9HTv67xaWRFcmeQilAS # F72E9oyFa65OTxdXoOpLD129692Rbaswbqy57P+qkj6MgBvTYFqBX9BZpnCOlCjw # YGQVWTj34fSxUtzxKISRI4wjvYT212T0deqhpJ/Oy1BlDik7WNiNncVZRHD2RfbH # qVZI37LaFGlSytG4z6VS7nUTGZLGay/IWEQIEwQ6AktWm696mSTKono680z7ZK2U # OGFneQVKagfzOIqD5ZFvlZjvC6z5181/oSEFtn5MMp71HoNd4ABYOrzldFUy0CPI # ruBB7yagUomtvLPFWux3OcA/pbJMK6EdAowrTQxxG1UCAwEAAaOCAbQwggGwMB8G # A1UdIwQYMBaAFCmRYP+KTfrr+aZquM/55ku9Sc4SMB0GA1UdDgQWBBTChARqgCg3 # JCwDL3vvWQz8tzA8XjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADATBgNV # HSUEDDAKBggrBgEFBQcDAzARBglghkgBhvhCAQEEBAMCBBAwRgYDVR0gBD8wPTA7 # BgwrBgEEAbIxAQIBAwIwKzApBggrBgEFBQcCARYdaHR0cHM6Ly9zZWN1cmUuY29t # b2RvLm5ldC9DUFMwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9j # YS5jb20vQ09NT0RPUlNBQ29kZVNpZ25pbmdDQS5jcmwwdAYIKwYBBQUHAQEEaDBm # MD4GCCsGAQUFBzAChjJodHRwOi8vY3J0LmNvbW9kb2NhLmNvbS9DT01PRE9SU0FD # b2RlU2lnbmluZ0NBLmNydDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2Rv # Y2EuY29tMCUGA1UdEQQeMByBGmZsb3JpYW4uZmVsZGhhdXNAZ21haWwuY29tMA0G # CSqGSIb3DQEBCwUAA4IBAQBPLYiedwvRO7FPhAkDLqCKNWwRgRWaP5fA6X/bYjcu # eV2oAAaPGdhee5mZgpjqvzLDxLCZiLJ/Z9wCAhiK9C42Lgahbe7XOIM8NDV/MmGn # Mo2Ba+X9KYmyVlRV6bQsNDpnW4t7mWkh4JpV2LZbLluSrJ88SAqCsrZb1C49H5m5 # YdwFZUSIL8P19MSCDxPr0OC/3qX0dFcSEBIIBKtKF3mm9/Yind3SgkxoPfxViX2D # eK80uOmm2Gb7bOhSuzqjkvDG0INsF4zyTX4HldBJmQfKeQiD8RlF6DpUcm0AoChM # qCLwiJOHaHHYOOS7Busif3LkvIKd+tWtrg5fw7gZ0fU3MIIF4DCCA8igAwIBAgIQ # LnyHzA6TSlL+lP0ct800rzANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UEBhMCR0Ix # GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEa # MBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0Eg # Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTMwNTA5MDAwMDAwWhcNMjgwNTA4 # MjM1OTU5WjB9MQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVz # dGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRl # ZDEjMCEGA1UEAxMaQ09NT0RPIFJTQSBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqG # SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmmJBjd5E0f4rR3elnMRHrzB79MR2zuWJX # P5O8W+OfHiQyESdrvFGRp8+eniWzX4GoGA8dHiAwDvthe4YJs+P9omidHCydv3Lj # 5HWg5TUjjsmK7hoMZMfYQqF7tVIDSzqwjiNLS2PgIpQ3e9V5kAoUGFEs5v7BEvAc # P2FhCoyi3PbDMKrNKBh1SMF5WgjNu4xVjPfUdpA6M0ZQc5hc9IVKaw+A3V7Wvf2p # L8Al9fl4141fEMJEVTyQPDFGy3CuB6kK46/BAW+QGiPiXzjbxghdR7ODQfAuADcU # uRKqeZJSzYcPe9hiKaR+ML0btYxytEjy4+gh+V5MYnmLAgaff9ULAgMBAAGjggFR # MIIBTTAfBgNVHSMEGDAWgBS7r34CPfqm8TyEjq3uOJjs2TIy1DAdBgNVHQ4EFgQU # KZFg/4pN+uv5pmq4z/nmS71JzhIwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQI # MAYBAf8CAQAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEQYDVR0gBAowCDAGBgRVHSAA # MEwGA1UdHwRFMEMwQaA/oD2GO2h0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9E # T1JTQUNlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMHEGCCsGAQUFBwEBBGUwYzA7 # BggrBgEFBQcwAoYvaHR0cDovL2NydC5jb21vZG9jYS5jb20vQ09NT0RPUlNBQWRk # VHJ1c3RDQS5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNv # bTANBgkqhkiG9w0BAQwFAAOCAgEAAj8COcPu+Mo7id4MbU2x8U6ST6/COCwEzMVj # EasJY6+rotcCP8xvGcM91hoIlP8l2KmIpysQGuCbsQciGlEcOtTh6Qm/5iR0rx57 # FjFuI+9UUS1SAuJ1CAVM8bdR4VEAxof2bO4QRHZXavHfWGshqknUfDdOvf+2dVRA # GDZXZxHNTwLk/vPa/HUX2+y392UJI0kfQ1eD6n4gd2HITfK7ZU2o94VFB696aSdl # kClAi997OlE5jKgfcHmtbUIgos8MbAOMTM1zB5TnWo46BLqioXwfy2M6FafUFRun # UkcyqfS/ZEfRqh9TTjIwc8Jvt3iCnVz/RrtrIh2IC/gbqjSm/Iz13X9ljIwxVzHQ # NuxHoc/Li6jvHBhYxQZ3ykubUa9MCEp6j+KjUuKOjswm5LLY5TjCqO3GgZw1a6lY # YUoKl7RLQrZVnb6Z53BtWfhtKgx/GWBfDJqIbDCsUgmQFhv/K53b0CDKieoofjKO # Gd97SDMe12X4rsn4gxSTdn1k0I7OvjV9/3IxTZ+evR5sL6iPDAZQ+4wns3bJ9ObX # wzTijIchhmH+v1V04SF3AwpobLvkyanmz1kl63zsRQ55ZmjoIs2475iFTZYRPAmK # 0H+8KCgT+2rKVI2SXM3CZZgGns5IW9S1N5NGQXwH3c/6Q++6Z2H/fUnguzB9XIDj # 5hY5S6cxggR7MIIEdwIBATCBkjB9MQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl # YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P # RE8gQ0EgTGltaXRlZDEjMCEGA1UEAxMaQ09NT0RPIFJTQSBDb2RlIFNpZ25pbmcg # Q0ECEQCw9VXqOdE2B8AFxQDu6bQIMAkGBSsOAwIaBQCgeDAYBgorBgEEAYI3AgEM # MQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQB # gjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBRNrBUbFLhN9ewT # Clgcxbtvgdcw5zANBgkqhkiG9w0BAQEFAASCAQA5NocCKZij1CWoaJ24wfPmM+oA # 7NhjDHktbAc0CKUlV1tnOuybHVrGpFyjewKR7MJ4EYdonI77KyNI6WHdQ0vVfcpK # sJsLxW5oXNcTErvTLRxgAcLi75CrlXjKC+H3E+0ahMC4cUPYQg3ggp8ljQQHGJqU # o2XECpLBZgC2BcnVINqEfOzI2T3KXtYQM/H2mDYNgAqKEuOVz5i4bwY5ECZJMavx # idHOUFzz58E6T50Uopj/BxPVWdvf2XNzu5t5ySu4N7y5/Y5cA5i8qZvz7A1VymfT # DU4NCC/eb+g1jc/KIFk3mqLfCPgTvCzXRTGstLBFvEOnXypl053k5c74H7UVoYIC # QzCCAj8GCSqGSIb3DQEJBjGCAjAwggIsAgEBMIGpMIGVMQswCQYDVQQGEwJVUzEL # MAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVU # aGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRy # dXN0LmNvbTEdMBsGA1UEAxMUVVROLVVTRVJGaXJzdC1PYmplY3QCDxaI8DklXmOO # aRQ5B+YzCzAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc # BgkqhkiG9w0BCQUxDxcNMTgwMTA4MDgxNTEzWjAjBgkqhkiG9w0BCQQxFgQUY3Id # v57Z+0tR0ZXQmO2lQSoZ9YUwDQYJKoZIhvcNAQEBBQAEggEAewt5OGG1T/2QvhNn # kNjauefcBaYhhWkr2jr2xFGMynTMwX9L6KRxAZRnjPfySDbo/SC4xMZKaw1gF7oL # T4H5I8aopUr09agHF/tpnWBrJRyC/2RWvjbfKiPhXuTriy0OCFBya0agETSNPzuS # b0wSUuJRcdoc6phltrfRT62+l4/hq91WVEop1u5FqpYONSD7Gce5ps/HgbymOkqC # tQy1aD+owR5BmvodcflWzwrgw1q/+im26oWHjj5Si2a3jK99b0BfB/yPG2aAGvS6 # KT2JuLbulW/sQPe3n39Ae+JkzDf+TSw3B1Wi4EbiXdpIQQzkZecadEMvnEkVL9n0 # yFt6Xg== # SIG # End signature block |