in C#

Amazon S3 + PowerShell

A Amazon Web Service  oferece diversos produtos e soluções em cloud computing, atualmente ela forneçe uma das mais completas gama de produtos e soluções do mercado de cloud. Uma dessas soluções é o Amazon Simple Storage Service (Amazon S3), que consiste em uma plataforma de armazenamento, onde é possível criar diversos containers e gerenciar arquivos, permissões, etc.

Durante esta semana estava estudando alguns serviços para um backup off-site, entre todos eles eu optei por utilizar o S3, a partir desta escolha comecei a estudar com quais ferramentas eu poderia automatizar a tranferência entre meus servidores e o armazenamento na S3. No mercado exitem diversas ferramentas para isso, entre elas optei por utilizar o SDK da Amazon e PowerShell, no qual eu consegui escrever a rotina que eu precisava e configurar esse script como uma tarefa agendada do windows server.

Começando

Para começar vamos precisar fazer download do SDK da Amazon para .NET, eu poderia ter feito um programa com C# utilizando o SDK da Amazon, mas como a idéia é utilizar e aprender a usar PowerShell, utilizei ele para escrever um script que copia todos os arquivos de um diretorio para a S3.

Inicialmente eu utilizei um método de upload ( .Upload ) do SDK que funcionava bem para arquivos pequenos, entretanto, como a idéia era fazer backup de grandes arquivos (+de 1G) eu precisei utilizar uma feature da API da Amazon chamada Multipart Upload (Using the AWS SDK for .NET for Multipart Upload) nela, eu particiono o arquivo, faço upload de todas as partes, e, no final do upload eu utilizo um comando que une todas as partes, criando a versão final do arquivo.

Para iniciar o script vou incluir a dll do SDK da Amazon:

Add-Type -Path "C:\Program Files (x86)\AWS SDK for .NET\bin\AWSSDK.dll"

Como segundo passo do meu script adicionarei as variáveis de acesso da API (com um usuário que eu criei e gerei a chave de acesso) e também criarei a variável com o Bucket que eu criei na S3

$secretKeyID="colocar secret key"
$secretAccessKeyID="colocar access key"
$bucket="NomeDoBucket"

O proximo passo do script é definir qual diretório eu farei o backup (“C:\teste”) e qual será o nome do diretório final no Bucket da S3 (“Backup_yyyy_mm_dd”):

$backup_directory="C:\teste"
$new_folder_format = Get-Date -uformat "Backup_%Y_%m_%d/"

Agora eu criarei um laço entre todos os arquivos do diretório, dentro desse laço nos faremos todo o trabalho de upload dos arquivos:

foreach($file in Get-ChildItem -Path $backup_directory){

#script de upload

}
Write-Host "Backup Finalizado"

Dentro desse laço, para cada arquivo eu farei o seguinte:

Escrever o nome do arquivo e instanciar o Cliente de acesso para a AWS

Write-Host $file.fullname
$client=[Amazon.AWSClientFactory]::CreateAmazonS3Client($secretKeyID,$secretAccessKeyID)

Em um segundo passo eu defino os nomes do arquivo final e o nome do arquivo fisico para fazer o upload.

$finalName=$new_folder_format+$file.name
$fileFullPath=$file.fullname

Também instancio um List de UploadPartResponse e uma instancia do InitiateMultipartUploadRequest para iniciarmos o upload.

$uploadResponses = New-Object "System.Collections.Generic.List[Amazon.S3.Model.UploadPartResponse]"

$initRequest=New-Object "Amazon.S3.Model.InitiateMultipartUploadRequest"
[void] $initRequest.WithBucketName($bucket)
[void] $initRequest.WithKey($finalName)

Com isso já podemos iniciar o MultipartUpload com o $client passando o $initRequest, tambem instancio algumas variaveis de controle, que utilizaremos para fazer upload de 5Mb por parte do arquivo.

$initResponse =$client.InitiateMultipartUpload($initRequest)

$fileInfo = New-Object -TypeName System.IO.FileInfo $arquivoFisico
$lengh= $fileInfo.Length
$partSize= 5242880

$filePosition =New-Object "Long"
$filePosition = 0

Com essas variaveis eu faço um loop entre as partes do arquivo para fazer upload de cada 5Mb

for ( $partNumber = 1; $filePosition -le $lengh;  $partNumber++)
{
	$uploadRequest = New-Object "Amazon.S3.Model.UploadPartRequest"
	[void] $uploadRequest.WithBucketName($bucket)
	[void] $uploadRequest.WithKey($finalName)
	[void] $uploadRequest.WithUploadId($initResponse.UploadId)
	[void] $uploadRequest.WithPartNumber($partNumber)
	[void] $uploadRequest.WithPartSize($partSize)
	[void] $uploadRequest.WithFilePosition($filePosition)
	[void] $uploadRequest.WithFilePath($fileFullPath)

	$uploadResponses.Add($client.UploadPart($uploadRequest))
	$filePosition += $partSize
	Write-Host $partNumber
}

Finalmente podemos juntar todas as partes do upload, para isso utilizamos o seguinte comando:

$compRequest = New-Object "Amazon.S3.Model.CompleteMultipartUploadRequest"
[void] $compRequest.WithBucketName($bucket)
[void] $compRequest.WithKey($finalName)
[void] $compRequest.WithUploadId($initResponse.UploadId)
[void] $compRequest.WithPartETags($uploadResponses)

$completeUploadResponse =$client.CompleteMultipartUpload($compRequest)

Por ultimo precisamos fechar o laço que fizemos entre cada arquivo do diretorio e escrevo uma mensagem de finalizado, com isso o script ficou da seguinte maneira:

Add-Type -Path "C:\Program Files (x86)\AWS SDK for .NET\bin\AWSSDK.dll"

$secretKeyID="secret key"
$secretAccessKeyID="access key"
$bucket="BucketName"

$backup_directory="C:\teste"
$new_folder_format = Get-Date -uformat "Backup_%Y_%m_%d/"

foreach($file in Get-ChildItem -Path $backup_directory){
	Write-Host $file.fullname
	$client=[Amazon.AWSClientFactory]::CreateAmazonS3Client($secretKeyID,$secretAccessKeyID)

	$finalName=$new_folder_format+$file.name
	$fileFullPath=$file.fullname

	$uploadResponses = New-Object "System.Collections.Generic.List[Amazon.S3.Model.UploadPartResponse]"

	$initRequest=New-Object "Amazon.S3.Model.InitiateMultipartUploadRequest"
	[void] $initRequest.WithBucketName($bucket)
	[void] $initRequest.WithKey($finalName)

	$initResponse =$client.InitiateMultipartUpload($initRequest)

	$fileInfo = New-Object -TypeName System.IO.FileInfo $fileFullPath
	$lengh= $fileInfo.Length
	$partSize= 5242880

	$filePosition =New-Object "Long"
	$filePosition = 0

	for ( $partNumber = 1; $filePosition -le $lengh;  $partNumber++)
	{
		$uploadRequest = New-Object "Amazon.S3.Model.UploadPartRequest"
		[void] $uploadRequest.WithBucketName($bucket)
		[void] $uploadRequest.WithKey($finalName)
		[void] $uploadRequest.WithUploadId($initResponse.UploadId)
		[void] $uploadRequest.WithPartNumber($partNumber)
		[void] $uploadRequest.WithPartSize($partSize)
		[void] $uploadRequest.WithFilePosition($filePosition)
		[void] $uploadRequest.WithFilePath($fileFullPath)

		$uploadResponses.Add($client.UploadPart($uploadRequest))
		$filePosition += $partSize
		Write-Host $partNumber
	}

	$compRequest = New-Object "Amazon.S3.Model.CompleteMultipartUploadRequest"
	[void] $compRequest.WithBucketName($bucket)
	[void] $compRequest.WithKey($finalName)
	[void] $compRequest.WithUploadId($initResponse.UploadId)
	[void] $compRequest.WithPartETags($uploadResponses)

	$completeUploadResponse =$client.CompleteMultipartUpload($compRequest)
}
Write-Host "Backup Finalizado"

O  script também pode ser encontrado no meu GitHub: https://gist.github.com/2691325

Para executar o script eu criei uma tarefa agendada, para isso eu coloquei o seguinte comando:

Caso o script apresente erro de segurança ao executar, é necessário habilitar a execução de scripts na maquina, abra um prompt do PowerShell e utilize o seguinte comando:

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned

Espero que este post seja útil,

estou a disposição para dúvidas, críticas e sugestões.

abs

Rodolfo

  • http://www.rodolfofadino.com.br/2012/07/amazon-ec2-powershell/ Amazon EC2 + PowerShell | // Rodolfo Fadino

    […] […]

  • Alessandro

    Mestre, sou analista de suporte e meu diretor de ti, mandou eu fazer o bkp pelo shellpower para amzon s3. mesmo com este link , não consigo fazer tem alguma video aula que possa me ajudar. tambem não falo ingles.obrigado.