Upload multiple folders from local storage to Azure as new containers with folder contents - azure

We have Azure Blob Storage Accounts with 100s of containers. The file structure is something like below:
container_01
|
--somemedia.jpg
--anothermedia.jpg
container_02
|
--secondcontainersmedia.jpg
--andSoOn
--AndSoOnAndSoOn
My client wants to download all of the containers to local storage so that if necessary they can be re-uploaded to Azure. After doing some research I found this blog post. Updating the script from there to suit my needs (just updating from AzureRM to AZ and my personal connection and local path), I came up with the following suitable script for downloading the files.
$destination_path = 'C:\Storage Dump Test'
$connection_string = '[Insert Connection String]'
$storage_account = New-AzStorageContext -ConnectionString $connection_string
$containers = Get-AzStorageContainer -Context $storage_account
Write-Host 'Starting Storage Dump...'
foreach ($container in $containers)
{
Write-Host -NoNewline 'Processing: ' . $container.Name . '...'
$container_path = $destination_path + '\' + $container.Name
if(!(Test-Path -Path $container_path ))
{
New-Item -ItemType directory -Path $container_path
}
$blobs = Get-AzStorageBlob -Container $container.Name -Context $storage_account
Write-Host -NoNewline ' Downloading files...'
foreach ($blob in $blobs)
{
$fileNameCheck = $container_path + '\' + $blob.Name
if(!(Test-Path $fileNameCheck ))
{
Get-AzStorageBlobContent `
-Container $container.Name -Blob $blob.Name -Destination $container_path `
-Context $storage_account
}
}
Write-Host ' Done.'
}
Write-Host 'Download complete.'
So now I have a directory on my local storage with hundreds of folders containing media items. I need to create a PS script (or find some other way) to basically do the opposite-- take all the folders in that directory, create containers using the names of the folders, and upload the items within each folder to the appropriate container.
How should I start going about this?

You'd have a lot more success, quicker, using azcopy instead of working with the azure cmdlets. To copy:
azcopy copy '<local-file-path>' 'https://<storage-account-name>.<blob| dfs>.core.windows.net/<container-name>/<blob-name>'
It can also create containers:
azcopy make 'https://mystorageaccount.blob.core.windows.net/mycontainer'
azcopy can download an entire container without you having to specify each file. Use --recursive
See: https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-v10

Related

Azure PowerShell script to compare files in the blob with local destination and download files which is not available in the destination

Could you please help me with Azure PowerShell script to compare files in the blob with local destination and download files which is not available in the destination.
I tried some, but able to get the answer.
$blobNames = Get-Content
For ($i=0; $i -lt $blobNames.Length; $i++) {
$blob = $blobNames[$i]
Write-Host "Downloading $blob. Please wait."
Get-AzStorageBlobContent -Blob $blob -Container $containerName -Destination $destination -Context $context -Verbos
PowerShell script to compare files in the blob with local destination and download files which is not available in the destination:
I've created a script and it works for me:
$ContainerName ='<containername>'
$destination_path = 'C:\Users\xxxx\Desktop\blobcheck' #pathtobedownloaded
$Ctx = New-AzureStorageContext '<storageaccount>' -StorageAccountKey 'accesskey'
$Blobs = Get-AzureStorageBlob -Container $ContainerName -Context $Ctx
$localfile = get-childitem -LiteralPath C:\Users\xxxxxx
For ($i=0; $i -lt $Blobs.Length; $i++) {
if($Blobs[$i].Name -eq $localfile){
Write-Host "Presented"
}
else{
$blob = $Blobs[$i].Name
Write-Host "Downloading $blob. Please wait."
Get-AzureStorageBlobContent -Blob $blob -Container $ContainerName -Destination $destination_path -Context $Ctx -Verbose
}
}
Compared and downloaded the files which is not present in my local folder:
Files uploaded in My Local folder (destination_path):
Prompting to rewrite if the file is already exists as shown below:
If the prompt is not needed, then you can simply use
DisplayAlerts = FALSE
Note: Get storage access key from Azure Portal:
Goto <Storageaccount> -> Access keys

How to move certain files from one storage blob container to another?

I have been trying to find the best way to do the following:
I need to move a large amount of json files that are named following the format "yyyymmdd-hhmmss.json" from one blob container to another that's in another storage account. These files are nested inside several different folders.
I only have to move the files that were created (or are named) before a certain date, for example: move all files that were created/are named before 01/01/2022.
What would be the best way to do so quickly? This is a one-time migration so it won't be recurring.
To copy files in bulk from a Source to a Destination Blob Container:
Connect-AzAccount
Get-AzSubscription
Select-AzSubscription -Subscription "My Subscription"
$srcResourceGroupName = "RG-DEMO-WE"
$srcStorageAccountName = "storageaccountdemowe"
$srcContainer = "sourcefolder"
$blobName = "dataDisk.vhd"
$destResourceGroupName = "RG-TRY-ME"
$destStorageAccountName = "storageaccounttryme"
$destContainer = "destinationfolder"
# Set Source & Destination Storage Keys and Context
$srcStorageKey = Get-AzStorageAccountKey -Name $srcStorageAccountName -ResourceGroupName $srcResourceGroupName
$destStorageKey = Get-AzStorageAccountKey -Name $destStorageAccountName -ResourceGroupName $destResourceGroupName
$srcContext = New-AzStorageContext -StorageAccountName $srcStorageAccountName -StorageAccountKey $srcStorageKey.Value[0]
$destContext = New-AzStorageContext -StorageAccountName $destStorageAccountName -StorageAccountKey $destStorageKey.Value[0]
# Optional step
New-AzStorageContainer -Name $destContainer -Context $destContext
# The copy operation
$copyOperation = Start-AzStorageBlobCopy -SrcBlob $blobName `
-SrcContainer $srcContainer `
-Context $srcContext `
-DestBlob $blobName `
-DestContainer $destContainer `
-DestContext $destContext
REF: https://www.jorgebernhardt.com/copy-blob-powershell/
Since you need to do individual files based on Date, instead of the Start-AzStorageBlobCopy the best is following the Microsoft Documentation with Async az storage file copy:
az storage file copy start --destination-path
--destination-share
[--account-key]
[--account-name]
[--connection-string]
[--file-endpoint]
[--file-snapshot]
[--metadata]
[--sas-token]
[--source-account-key]
[--source-account-name]
[--source-blob]
[--source-container]
[--source-path]
[--source-sas]
[--source-share]
[--source-snapshot]
[--source-uri]
[--timeout]
REF: https://learn.microsoft.com/en-us/cli/azure/storage/file/copy?view=azure-cli-latest
The code to loop through the files based on date I'll leave to the reader, eg:
Get-ChildItem | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)}
You can iterate each blob in the source container (No matter how the folder structure is, as blob folders are simply virtual), and you can parse the name of the blob to filter blobs matching the pattern "yyyymmdd-hhmmss" and find the date and if it is older than the date that you wish to choose as a condition, you can easily copy the blob from your source to destination container, and finally delete the blob from the source container. Not sure about power shell, but its easy with any supported programming language.
Here's an example of doing this with .Net:
BlobContainerClient sourceContainerClient = new BlobContainerClient("<source-connection-string>", "<source-container-name>");
BlobContainerClient destinationContainerClient = new BlobContainerClient("<destination-connection-string>", "<destination-container-name>");
var blobList = sourceContainerClient.GetBlobs();
DateTime givenDateTime = DateTime.Now;
foreach (var blobItem in blobList)
{
try
{
var sourceBlob = sourceContainerClient.GetBlobClient(blobItem.Name);
string blobName = sourceBlob.Uri.Segments.Last().Substring(0, sourceBlob.Uri.Segments.Last().IndexOf('.'));
if (DateTime.Compare(DateTime.ParseExact(blobName, "yyyyMMdd-hhmmss", CultureInfo.InvariantCulture), givenDateTime) < 0)
{
var destinationBlob = destinationContainerClient.GetBlockBlobClient(blobName);
destinationBlob.StartCopyFromUri(sourceBlob.Uri);
sourceBlob.Delete();
}
}
catch { }
}

How to get list of Azure container files?

I'm working on PS script to list all Storage Accounts, which contains files with a modified date less than < X.
I'm able to list all SA containers, it's not a big deal but I'm not sure how to get further and list all files inside a particular container.
$storageAccCtx = (Get-AzStorageAccount -Name acc_name -ResourceGroupName acc_rg).Context
Get-AzStorageContainer -Context $storageAccCtx
I couldn't find any cmdlet for this.
Could anyone, please, advise what should I use next? Thanks.
Once you have the StorageContext, you can use this below Azure Storage Management Cmdlet to list all BlockBlob.
Get-AzStorageBlob -Container containerName -Context $storageAccCtx
To get list of all Azure Storage Management Cmdlet, please follow this documentation https://learn.microsoft.com/en-us/powershell/module/az.storage/?view=azps-4.8.0
You can use Get-AzStorageBlob to list the blobs in a storage container, the cmdlet is documented here. In a script you could use this cmdlet as follows to return all the blobs older than a particular date:
$CutOffDate = Get-Date -Year 2020 -Month 10 -Day 19
$OldBlobs = #()
$StorageAccounts = Get-AzStorageAccount
foreach ($StorageAccount in $StorageAccounts) {
$Containers = Get-AzStorageContainer -Context $StorageAccount.Context
foreach ($Container in $Containers) {
$ContainerBlobs = Get-AzStorageBlob -Container $Container.Name -Context $StorageAccount.Context
$OldBlobs += $ContainerBlobs | Where-Object { $_.LastModified -lt $CutOffDate }
}
}
$OldBlobs

Escaping characters on AzureBlobContent

I have got a problem to set my content in AzureBlobStorage.
In local, I have succeeded to replace characters for each files in a directory.
$sourceFolder = "C:\MyDirectory"
$targetFolder = "C:\MyDirectoryEncodeded"
$fileList = Dir $sourceFolder -Filter *.dat
MkDir $targetFolder -ErrorAction Ignore
ForEach($file in $fileList) {
$file | Get-Content | %{$_ -replace '"',''} | %{$_ -replace ',','.'} | Set-Content -Path "tempDirectory\$file"
$newFile = Get-Content "tempDirectory\$file"
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines("targetDirectory\$file" , $newFile,$Utf8NoBomEncoding)
}
exit
But now, I need to do the same in Microsoft Azure.
I get the content into an Azure Blob Storage, I escape characters, I encoding my file in UTF-8NoBom and then I set the encode file into a new Blob Directory.
Nevertheless, I faced an issue when I want to set the new content with escape characters (First line in my loop).
$storageContext = New-AzureStorageContext -ConnectionString "DefaultEndpointsProtocol=https;AccountName=<myAccountName>;AccountKey=<myAccountKey>;"
$sourceFolder = Get-AzureStorageBlob -Container "datablobnotencoded" -Blob "*.dat" -Context $storageContext
$targetFolder = Get-AzureStorageBlob -Container "datablob" -Context $storageContext
MkDir $targetFolder -ErrorAction Ignore
ForEach($file in $sourceFolder) {
Get-AzureStorageBlob -Container "datablobnotencoded" -Blob $file.Name -Context $storageContext | Get-AzureStorageBlobContent | %{$_ -replace '"',''} | %{$_ -replace ',','.'} | Set-AzureStorageBlobContent -File $file.Name -Context $storageContext -CloudBlob $file
$newFile = Get-AzureStorageFileContent -Path $file
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($file , $newFile, $Utf8NoBomEncoding)
}
I've got this error:
Set-AzureStorageBlobContent : Cannot bind parameter 'CloudBlob'.
Cannot convert the
"Microsoft.WindowsAzure.Commands.Storage.Model.ResourceModel.AzureStorageBlob"
value of type
"Microsoft.WindowsAzure.Commands.Storage.Model.ResourceModel.AzureStorageBlob"
to type "Microsoft.WindowsAzure.Storage.Blob.CloudBlob". At line:7
char:264
+ ... lobContent -File $file.Name -Context $storageContext -CloudBlob $file
+ ~~~~~
+ CategoryInfo : InvalidArgument: (:) [Set-AzureStorageBlobContent], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.WindowsAzure.Commands.Storage.Blob.SetAzureBlobContentCommand
Thank you for your answers!
There are some mistakes in your powershell scripts:
1.You may misunderstand the usage of Get-AzureStorageBlobContent, it's used to download blob to local, you cann't get the content of the blob, more details refer here.
2.In the loop, you used $newFile = Get-AzureStorageFileContent -Path $file, the Get-AzureStorageFileContent cmdlet is for file share storage, not for the blob storage.
You can use Get-AzureStorageBlobContent to download the blobs to a local folder, then operate on the local file which is downloaded from blob storage. After the file is modified, you can use Set-AzureStorageBlobContent to upload the local files to the specified azure blob storage.
Sample code as below, and works fine at my side:
$context = New-AzureStorageContext -ConnectionString "xxxx"
#download the blobs in specified contianers
$sourceFolder_blob = Get-AzureStorageBlob -Container "test-1" -Blob "*.txt" -Context $context
#the target azure container, which you want to upload the modifed blob to
$taget_container="test-2"
#the local path which is used to store the download blobs, and make sure the folders exist before use.
$sourceFolder_local="d:\test\blob1\"
$targetFolder_local="d:\test\blob2\"
foreach($file in $sourceFolder_blob)
{
#download the specified blob to local path
Get-AzureStorageBlobContent -Container "test-1" -Blob $file.name -Destination $sourceFolder_local -Context $context
#get the local file path
$local_file_path=$sourceFolder_local + $file.name
#set content to the file in target local folder
$local_target_file_path = "$targetFolder_local"+$file.name
#since the files are downloaded to local, you can any operation for the local file
Get-Content $local_file_path | %{$_ -replace '-','!'} | %{$_ -replace ',','.'} | Set-Content -Path $local_target_file_path
$newFile = Get-Content -Path $local_target_file_path
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($local_target_file_path , $newFile,$Utf8NoBomEncoding)
#the last step, upload the modified file to another azure container
Set-AzureStorageBlobContent -File $local_target_file_path -Context $context -Container $taget_container
}

How to break a lease on Blob Storage in Azure with PowerShell?

How do I break a lease on an item in Blob Storage utilizing PowerShell?
I'm receiving the following when trying to upload something over the current image:
Add-AzureRmVhd : The remote server returned an error: (412) There is currently a lease on the blob and no lease ID was specified in the request..
At line:1 char:1
+ Add-AzureRmVhd -Destination $osDiskUri -LocalFilePath $localFileName ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Add-AzureRmVhd], StorageException
+ FullyQualifiedErrorId : Microsoft.Azure.Commands.Compute.StorageServices.AddAzureVhdCommand
Login to the old portal and navigate to the Virtual Machines then the Images tab the url will be https://manage.windowsazure.com/#yourname.onmicrosoft.com#Workspaces/VirtualMachineExtension/images. Select the image and choose Delete on the bottom.
After that go to your storage and delete it.
You can also try the following which will remove blobs for a given container and then remove the container.
Add-AzureAccount
Get-AzureSubscription | Format-Table SubscriptionName, IsDefault, IsCurrent, CurrentStorageAccountName
$SubscriptionName = 'Your subsscription name'
Select-AzureSubscription -SubscriptionName $SubscriptionName
Get-AzureSubscription -Default
Get-AzureStorageAccount | Format-Table -Property StorageAccountName, Location, AccountType, StorageAccountStatus
$StorageAccountName = "Your storage account"
$StorageAccountKey = (Get-AzureStorageKey -StorageAccountName $StorageAccountName).Primary
$ContainerName = "Your container name"
$Context = New-AzureStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $StorageAccountKey
#Get a reference to all the blobs in the container.
$blobs = Get-AzureStorageBlob -Container $ContainerName -Context $Context
#Remove lease on each Blob
$blobs | %{$_.ICloudBlob.BreakLease()}
#Delete blobs in a specified container.
$blobs| Remove-AzureStorageBlob
Remove-AzureStorageContainer -Container $ContainerName -Context $Context
If you want to break a seal on a blob you can use the How to break the locked lease of blob storage in Microsoft Azure (PowerShell)
$key = (Get-AzureRmStorageAccountKey -ResourceGroupName $selectedStorageAccount.ResourceGroupName -name $selectedStorageAccount.StorageAccountName -ErrorAction Stop)[0].value
$storageContext = New-AzureStorageContext -StorageAccountName $selectedStorageAccount.StorageAccountName -StorageAccountKey $key -ErrorAction Stop
$storageContainer = Get-AzureStorageContainer -Context $storageContext -Name $ContainerName -ErrorAction Stop
$blob = Get-AzureStorageBlob -Context $storageContext -Container $ContainerName -Blob $BlobName -ErrorAction Stop
$leaseStatus = $blob.ICloudBlob.Properties.LeaseStatus;
If($leaseStatus -eq "Locked")
{
$blob.ICloudBlob.BreakLease()
Write-Host "Successfully broken lease on '$BlobName' blob."
}
Else
{
#$blob.ICloudBlob.AcquireLease($null, $null, $null, $null, $null)
Write-Host "The '$BlobName' blob's lease status is unlocked."
}
If you want to a script for ARM resources you can use the
How to break the locked lease of blob storage by ARM in Microsoft Azure(PowerShell)
The lease is likely from something like a VM, or something else using the Blog Storage. As a result manually releasing the lease could cause problems.
With that said, the following PowerShell command should do the trick:
Get-AzureRmStorageAccount -Name "STORAGE_ACCOUNT_NAME" | Get-AzureStorageBlob -name "CONTAINER_NAME").ICloudBlob.BreakLease()
If its a VM, you should see the following post on removing the disk:
Cannot delete blob: There is currently a lease on the blob and no lease ID was specified in the request
However, if you simply want to replace the drive used by every machine that uses the given blob, stopping the VM, releasing the lease, uploading a new image, and starting the VM appears to work.

Resources