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 donwload 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










