Nesse post vou mostrar como publicar um aplicação feita em .NET Core 6 no Azure Web App utilizando GitHub Actions.
A aplicação que faremos deploy pode ser encontrada nesse link e ela consiste em calcular o Fatorial de um número utilizando Recursão como solução para o problema. Aproveitando, vou deixar abaixo uma citação muito interessante sobre recursão.
“Os loops podem melhorar o desempenho do seu programa. A recursão melhorar o desempenho do seu programador. Escolha o que for mais importante para a sua situação.”
Leigh Coldwell (Retirado do livro: Entendendo Algoritmos)
Sem mais delongas, vamos para o passo a passo.
Primeiramente, vamos acessar o portal do Azure e criar um Web App.
Clique em Create a Resource e na barra de pesquisa digite Web App em seguida clique em Create.
Agora vamos preencher os dados necessários para criar nosso Web App:
Selecione ou crie um Resource Group;
Digite um nome para sua Web App (lembrando que esse nome precisa ser único);
No Runtime stack, selecione .NET 6 (LTS);
Em Operating System, selecione Linux;
Em Region, selecione a região que mais lhe agrada;
Em SKU and Size selecione o plano que deseja (existe um plano Free);
E finalize clicando em Next:Deployment;
Nesse momento começa a configuração do Deploy contínuo utilizando GitHub Actions.
Em Continuous deployment, marque Enable;
Faça o login da sua conta GitHub;
Na sequência, selecione a Organização, o Repositório e a Branch que deseja que ocorra o deploy;
Agora clique em Review + Create (Poderíamos customizar mais configurações no nosso Web App, entretanto como o propósito desse artigo é demonstrar como fazer o deploy, ja vamos finalizar nossa customização aqui).
Assim que terminar o deploy, podemos clicar no link (https://<nome_do_seu_webapp>.azurewebsites.net) para acessar a nossa aplicação. Feito isso observe que ocorre um HTTP ERROR 404, entretanto veja que nossa aplicação esta funcionando, e para confirmar isso basta fazer um curl na API, conforme exemplo abaixo:
curl -X 'GET' \ 127 ✘ 15:53:27
'https://web-factorial.azurewebsites.net/Factorial?number=5' \
-H 'accept: */*'
The factorial of the number 5 is 120.%
Para deixarmos algo mais amigável, note que o deploy esta considerando o environment como Produção e no nosso código por padrão (e principalmente, por segurança), o Swagger só esta habilitado para ambiente de desenvolvimento, então se voltamos ao nosso código no arquivo Program.cs e comentar o seguinte trecho de código:
Olá pessoal, tudo bem? Nesse post vamos aprender como fazer um redimensionamento de imagens altamente escalável e muito simples utilizando Azure Functions e Azure Storage.
Primeiramente vamos criar uma conta de Storage no Azure e na sequência 2 containers no serviço de Blob. O primeiro container vai se chamar images (onde nossa Function vai monitorar), e o segundo vai se chamar thumbnails (onde salvaremos a nossa imagem redimensionada).
Dica: Caso esteja utilizando o sistema operacional Windows, você pode utilizar o Azure Storage Emulator para simular o serviço de Storage do Azure.
Logo depois de criarmos o serviço de Storage (seja no Azure ou simulando localmente através do Azure Storage Emulator), vamos abrir o Terminal (Ctrl + ‘) do Visual Studio Code, e digitarmos:
func init
Note que vão aparecer 4 opções de seleção, nesse caso, selecione a opção “dotnet”, e então, espere que termine a criação do nosso Function App.
Logo depois, nosso próximo passo será o de criar nossa Azure Function, e para isso, utilize o comando abaixo:
func new
Agora, dentre as diversas opções de Azure Functions, vamos escolher o tipo BlobTrigger e daremos a ela o nome de ImageResizerTrigger.
Seguindo, abra o arquivo .csproj do seu projeto e adicione o package da SixLabors.ImageSharp nele, igual a linha abaixo:
Na sequência o Visual Studio Code pedirá para que você faça um Restore na aplicação.
Assim que o Restore finalizar, abra o arquivo ImageResizerTrigger.cs e substitua-o com o código abaixo:
public static void Run([BlobTrigger("images/{name}", Connection = "AzureWebJobsStorage")]Stream myBlob, string name,
[Blob("thumbnails/{name}", FileAccess.Write)] Stream outputBlob, ILogger log)
{
log.LogInformation($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");
using (var image = Image.Load(myBlob))
{
var resizeOptions = new ResizeOptions
{
Size = new Size(640, 480),
Compand = true,
Mode = ResizeMode.Max
};
image.Mutate(ctx => ctx.Resize(resizeOptions));
image.Save(outputBlob, new JpegEncoder { Quality = 80 });
}
}
Dessa forma, note no nosso código, que a nossa Azure Function estará sempre olhando para o containerimages, e seu “output” será sempre no containerthumbnails.
Importante: A propriedade Connection esta “setada” com o valor: AzureWebJobsStorage. Esse valor na verdade esta contido no arquivo local.settings.json e você deverá colocar a string de conexão do seu Storage no mesmo.
Assim, faça o upload de uma imagem no containerimages e note que a imagem será automaticamente redimensionada e salva no containerthumbnails, conforme nós queríamos.
Olá pessoal, tudo bem? Neste post derradeiro sobre como criar um CRUD Serverless com Azure Functions, vamos aprender como orquestrar nossas API’s utilizando o Azure API Management. O intuito desse post não é explicar em detalhes esse serviço, mas sim em focar como podemos deixar nossas API’s mais seguras.
Primeiramente, acesse o https://portal.azure.com e clique em Create a Resource, e logo na sequência na barra de busca pesquise por API Management.
Preencha as informações necessárias e aguarde a criação do serviço terminar (isso leva em média 20 minutos). Agora nosso próximo passo será importar nossas API’s e para isso na barra esquerda clique em API’s e em seguida selecione Function App. No modal que aparecer clique em Browse, depois em Function App e então selecione a aplicação Serverless que publicamos na parte 3 do artigo, e após o preenchimento dos dados clique em Create.
Observe que você pode criar um sufixo para a chamada das API’s editando o campo API URL suffix. Outro ponto interessante é que ele nos mostra todas as nossas API’s (HttpTriggers) e quais desejamos importar (por padrão todas vem selecionadas).
Dessa forma, terminada a importação, vamos adicionar um “Produto” para a nossa API. Por padrão temos duas opções: Starter e Unlimited, tendo a possibilidade criarmos um customizado também caso necessário.
Após selecionado o produto, vamos clicar em Developer portal e logo abaixo do nome de login (provavelmente ADMINISTRATOR), selecione a opção Profile. Note que clicando nessa opção apareceram os dois produtos disponíveis (Starter e Unlimited) como duas Keys para cada um. Essas keys serão fundamentais para que nossas API’s funcionem pois sem elas, se tentarmos acessa-las aparecerá o seguinte erro:
{
"statusCode": 401,
"message": "Access denied due to invalid subscription key. Make sure to provide a valid key for an active subscription."
}
Então nosso próximo passo será autorizar a chamada das nossas API’s e para isso será necessário passar um Header da requisição chamado Ocp-Apim-Subscription-Key e como valor a Key do produto em questão. Note que se você fizer a chamada de alguma API ela vai funcionar perfeitamente.
Agora vamos implementar algumas Policies para nossa aplicação. Habilitaremos o CORS, na sequência as chamadas para um determinado range de IP’s e por fim restringir um numero abusivo de chamadas as nossas API’s em um pequeno espaço de tempo.
Para isso selecione nosso conjunto de API’s e clique em base nas opções de Inbound processing e em seguida adicione o código abaixo para habilitar o CORS (lembre-se que todo o código deve ser colocado dentro da tag de inbound):
Por último, com o código abaixo, restringiremos por 15 segundos o acesso as nossas API’s caso alguém acesse alguma delas 3 vezes seguidas em sequência.
Bom pessoal e assim finalizamos nossa série Criando um CRUD Serverless com Azure Functions.
E para finalizar deixo aqui um convite.
Que tal aprender mais sobre Docker, Kubernetes e a implementação de soluções baseadas em containers utilizando o Microsoft Azure, em um workshop que acontecerá durante um sábado todo (dia 04/04) em São Paulo Capital e implementando um case na prática?
Acesse então o link a seguir para efetuar sua inscrição (já incluso camiseta, emissão de certificado e almoço para os participantes) com o desconto: http://bit.ly/anp-docker-blog-ericson
Seguindo a nossa série em que vamos criar um CRUDServerless com Azure Functions, hoje desenvolveremos a listagem dos nossos registros, a publicação da nossa Function App no Azure e por fim a publicação de um site estático consumindo nossas API’s no Azure Storage.
Primeiramente vamos trabalhar na listagem dos registros, e portanto, começaremos criando nossa Function através do comando abaixo:
func new
Feito isso, como já é sabido, aparecerão diversas opções de Azure Functions, cada uma para um propósito diferente, e no nosso caso escolheremos o tipo HttpTrigger e então nós daremos a ela o nome de ListPeople.
Abra o arquivo criado (ListPeople.cs) e vamos altera-lo com o código abaixo:
public static async Task<IEnumerable<Person>> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
[Table("Person")]CloudTable cloudTable,
ILogger log)
{
log.LogInformation("ListPeople function started a request.");
var tableQuery = new TableQuery<Person>();
TableContinuationToken continuationToken = null;
TableQuerySegment<Person> tableQueryResult;
do
{
tableQueryResult = await cloudTable.ExecuteQuerySegmentedAsync(tableQuery, continuationToken);
continuationToken = tableQueryResult.ContinuationToken;
} while (continuationToken != null);
log.LogInformation("ListPeople function finished a request.");
return tableQueryResult.Results;
}
Observe que o código mudou levemente em relação as HttpTriggers anteriores. Nesse código mudamos o retorno da Função, sendo que agora ela retorna um IEnumerable de Person e para que isso seja possível utilizamos o comando TableQuerySegment para trazer todos os registros da nossa tabela.
Vamos agora executar nosso Function App utilizando o comando abaixo:
func start
Agora vamos testar nossa função de listagem de registros (para isso recomendo utilizar um Client REST da sua preferência) acessando o novo endpoint criado: ListPeople: [GET] http://localhost:7071/api/ListPeople, e observe ele retornou todos os registros cadastrados.
Ao contrário do que vimos na parte 1 e parte 2 do artigo, não criaremos nenhuma QueueTrigger, pois não iremos enfileirar nenhuma consulta, ao invés disso, vamos criar mais uma HttpTrigger e dessa vez será para trazer um único registro ao invés da listagem toda.
Novamente vamos utilizar o comando abaixo:
func new
Feito escolheremos novamente o tipo HttpTrigger e para essa função daremos o nome de GetPerson.
Abra o arquivo criado (GetPerson.cs) e vamos altera-lo com o código abaixo:
public static async Task<Person> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
[Table("Person")]CloudTable cloudTable,
ILogger log)
{
log.LogInformation("GetPerson function started a request.");
var partitionKey = "Person";
var rowKey = req.Query["id"];
TableOperation person = TableOperation.Retrieve<Person>(partitionKey, rowKey);
TableResult result = await cloudTable.ExecuteAsync(person);
log.LogInformation("GetPerson function finished a request.");
return (Person)result.Result;
}
Note que o código acima não é bem semelhante ao que já produzimos nas Funções anteriores e então vamos mais uma vez testar a nossa aplicação, utilizando o comando:
func start
Mais uma vez vamos acessar o novo endpoint criado: GetPerson: [GET] http://localhost:7071/api/GetPerson, (lembre-se de passar o RowKey via QueryString sob o parâmetro de nome id ), e observe ele retornou o registro cadastrado.
Agora finalmente vamos publicar tudo isso no Azure, e para isso (caso ainda não tenha instalado), vamos instalar a extensão do Azure Functions no VsCode e com ela instalada, você irá clicar na opção Deploy to Function App…, e em seguida selecione sua Subscription no Azure (caso possua mais de uma), nosso próximo passo será selecionar Create a New Function Appin Azure, feito isso ele irá lhe pedir um nome (necessita ser único globalmente), e por fim selecione a Region em que deseja fazer o deploy.
Agora é so aguardar alguns minutos para que o deploy seja finalizado no Azure.
Bônus
Para deixarmos nosso CRUD mais profissional podemos criar uma Single Page Application utilizando Angular e publicar isso no Azure, gastando apenas o custo de armazenamento, sem que seja cobrado qualquer custo de computação. Para realizarmos esse próximo passo, deixo abaixo um vídeo que eu publiquei no canal Azure na Prática fazendo um passo a passo de como fazer isso.
Por sim para quem tiver interesse todo o código da nossa Function App encontra-se no meu GitHub, inclusive contendo uma Single Page Application feita em Angular, pronta para ser publicada. Lembrando que no próximo e último artigo da série de posts, vou demonstrar como utilizarmos o AzureAPI Management pra a orquestração das nossas API’s criadas na nossa Function App.
E para finalizar deixo aqui um convite.
Que tal aprender mais sobre Docker, Kubernetes e a implementação de soluções baseadas em containers utilizando o Microsoft Azure, em um workshop que acontecerá durante um sábado todo (dia 04/04) em São Paulo Capital e implementando um case na prática?
Acesse então o link a seguir para efetuar sua inscrição (já incluso camiseta, emissão de certificado e almoço para os participantes) com o desconto: http://bit.ly/anp-docker-blog-ericson
Seguindo a nossa série em que vamos criar um CRUDServerless com Azure Functions, hoje desenvolveremos a parte de edição e de exclusão do nosso registro.
Primeiramente vamos trabalhar na edição, e portanto, começaremos criando nossa Function através do comando abaixo:
func new
Feito isso, como já é sabido, aparecerão diversas opções de Azure Functions, cada uma para um propósito diferente, e no nosso caso escolheremos o tipo HttpTrigger e então nós daremos a ela o nome de EditPerson.
Abra o arquivo criado (EditPerson.cs) e vamos altera-lo com o código abaixo:
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "put", Route = null)]HttpRequest req,
[Queue("EditPerson", Connection = "AzureWebJobsStorage")]IAsyncCollector<string> queueItem,
ILogger log)
{
log.LogInformation("EditPerson function started a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
await queueItem.AddAsync(requestBody);
log.LogInformation("EditPerson function finished a request.");
return new OkObjectResult($"Obrigado! Seu registro já esta sendo processado.");
}
Observe que o código não é muito diferente do código da nossa HttpTrigger que criamos na parte 1 do nosso post.
Dica: Observe que na chamada da nossa HttpTrigger existe uma propriedade chamada Route, ela serve para customizarmos a rota da nossa Function caso você queira, do contrário o padrão será sempre o nome da sua HttpTrigger.
Vamos agora executar nosso Function App utilizando o comando abaixo:
func start
Note que agora nós temos dois endpoints: CreatePerson: [POST] http://localhost:7071/api/CreatePerson e EditPerson: [PUT] http://localhost:7071/api/EditPerson e com isso já é possível testar nossa função de edição (para isso recomendo utilizar um Client REST da sua preferência), porém dessa vez precisaremos passar além das propriedades Name e Email as propriedades para encontrar o registro em si e elas são a PartitionKey e o RowKey. Com isso seu body deve ficar muito parecido com que se encontra logo abaixo:
Feito isso abra a ferramenta Microsoft Azure Storage Explorer e localize dentro de Storage Account o serviços de Queues e abrindo-o note que foi criada uma Queue chamada EditPerson. Abra a mesma e note que o body que passamos para o nosso endpoint estará armazenado na fila.
Agora nosso próximo passo, assim como na parte 1 do nosso post, será o de desenfileirar a mensagem e salva-la no nosso banco de dados e para isso vamos criar nossa próxima Azure Function, e para isso, novamente vamos utilizar o comando abaixo:
func new
Dessa vez escolheremos o tipo QueueTrigger e colocaremos o nome dela de EditPersonQueue e em seguida, vamos altera-la com o código abaixo:
public static void Run(
[QueueTrigger("EditPerson", Connection = "AzureWebJobsStorage")]string queueItem,
[Table("Person")]CloudTable cloudTable,
ILogger log)
{
log.LogInformation($"EditPersonQueue trigger function started.");
var data = JsonConvert.DeserializeObject<Person>(queueItem);
var tableOperation = TableOperation.InsertOrReplace(data);
cloudTable.ExecuteAsync(tableOperation);
log.LogInformation($"EditPersonQueue trigger function finished.");
}
Note que novamente não há muita mudança em relação nossa Queue Trigger criada no post anterior.
Com isso vamos executar nosso Function App novamente através do comando:
func start
E então abra novamente o Microsoft Azure Storage Explorer e note que a mensagem sumiu da fila EditPerson, e agora o registro encontra-se editado na nossa tabela Person.
Agora que a edição esta finalizada e funcionando perfeitamente, vamos para o próximo passo que é o de excluir o nosso registro, e para isso vamos novamente criar mais uma função através do comando abaixo:
func new
Novamente, escolha o tipo HttpTrigger e então nós daremos a ela o nome de DeletePerson.
Abra o arquivo criado (DeletePerson.cs) e vamos altera-lo com o código abaixo:
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "delete", Route = null)]HttpRequest req,
[Queue("DeletePerson", Connection = "AzureWebJobsStorage")]IAsyncCollector<string> queueItem,
ILogger log)
{
log.LogInformation("DeletPerson function started a request.");
await queueItem.AddAsync(
JsonConvert.SerializeObject(
new Person
{
PartitionKey = "Person",
RowKey = req.Query["id"]
}
)
);
log.LogInformation("DeletePerson function finished a request.");
return new OkObjectResult($"Obrigado! Seu registro já esta sendo processado.");
}
Observe que agora nossa HttpTrigger, ao contrario das criadas anteriormente, espera como body apenas o Partition Key (coloquei ele já direto no código, mas você pode passa-lo como parâmetro caso queira) e o RowKey que será passado via QueryString sob o parâmetro de nome id.
Vamos agora executar nosso Function App utilizando o comando abaixo:
func start
Note que agora nós temos três endpoints: DeletePerson: [DELETE] http://localhost:7071/api/DeletePerson, CreatePerson: [POST] http://localhost:7071/api/CreatePerson e EditPerson: [PUT] http://localhost:7071/api/EditPerson e com isso já é possível testar nossa função de exclusão (para isso recomendo utilizar um Client REST da sua preferência), porém dessa vez conforme precisaremos apenas do RowKey e com isso sua QueryString deve ficar muito parecido com que se encontra logo abaixo:
Feito isso abra a ferramenta Microsoft Azure Storage Explorer e localize dentro de Storage Account o serviços de Queues e abrindo-o note que foi criada uma Queue chamada DeletePerson. Abra a mesma e note que o body que passamos para o nosso endpoint estará armazenado na fila.
Agora nosso próximo passo, assim como na parte 1 do nosso post, será o de desenfileirar a mensagem e salva-la no nosso banco de dados e para isso vamos criar nossa próxima Azure Function, e para isso, novamente vamos utilizar o comando abaixo:
func new
Dessa vez escolheremos o tipo QueueTrigger e colocaremos o nome dela de DeletePersonQueue e em seguida, vamos altera-la com o código abaixo:
public static void Run(
[QueueTrigger("DeletePerson", Connection = "AzureWebJobsStorage")]string queueItem,
[Table("Person")]CloudTable cloudTable,
ILogger log)
{
log.LogInformation($"DeletePersonQueue trigger function started.");
var data = JsonConvert.DeserializeObject<Person>(queueItem);
var person = new DynamicTableEntity(data?.PartitionKey, data?.RowKey);
person.ETag = "*";
var tableOperation = TableOperation.Delete(person);
cloudTable.ExecuteAsync(tableOperation);
log.LogInformation($"DeletePersonQueue trigger function finished.");
}
Note que dessa vez houve uma certa mudança em relação nossa Queue Trigger criada no post anterior.
Aqui nos primeiros temos que recuperar o registro da nossa TablePerson, baseando-se na PartitionKey e no RowKey antes de mais nada e na sequência setar a propriedade ETag. Falando rapidamente sobre essa propriedade, ela consegue atuar no caso de concorrência de alterações de um registro, como aqui eu quero excluir o mesmo eu estou setando com o * e isso significa que eu estou dizendo para o Tables não se preocupar em monitorar essa exclusão.
Com isso vamos executar nosso Function App novamente através do comando:
func start
E então abra novamente o Microsoft Azure Storage Explorer e note que a mensagem sumiu da fila DeletePerson, e agora o registro também não existe mais na nossa tabela Person.
Bom pessoal, e com isso finalizamos a segunda parte do nosso CRUDServerless em que editamos e excluímos o nosso registro. No nosso próximo post vamos trabalhar na exibição dos registros já cadastrados e na publicação do nosso Function App no Azure.
E para finalizar deixo aqui um convite.
Que tal aprender mais sobre Docker, Kubernetes e a implementação de soluções baseadas em containers utilizando o Microsoft Azure, em um workshop que acontecerá durante um sábado todo (dia 04/04) em São Paulo Capital e implementando um case na prática?
Acesse então o link a seguir para efetuar sua inscrição (já incluso camiseta, emissão de certificado e almoço para os participantes) com o desconto: http://bit.ly/anp-docker-blog-ericson
No Visual Studio Code recomendo fortemente instalar a extensão do Azure Functions, pois ela vai nos ajudar bastante.
Assim que todas as instalações forem concluídas, vamos executar o Azure Storage Emulator (caso o mesmo já não esteja em execução), e então na sequência, vamos criar nosso projeto no Visual Studio Code, abrindo seu Terminal (Ctrl + ‘) e em seguida digitando o seguinte comando:
func init
Feito vão aparecer 4 opções de seleção, nesse caso, selecione a primeira opção “dotnet”, e então, espere que termine a criação do nosso Function App.
Nosso próximo passo será o de criar nossa primeira Azure Function, e para isso digite o comando abaixo:
func new
Feito isso apareceram diversas opções de Azure Functions, cada uma para um propósito diferente, e no nosso caso escolheremos o tipo HttpTrigger e daremos a ela o nome de CreatePerson.
Abra o arquivo criado (CreatePerson.cs) e observe que ele já veio com um código padrão. Esse código nada mais faz, do que esperar um parâmetro chamado name e no fim exibirá uma mensagem de boas vindas.
Note que a função possui um cabeçalho, e ele e de extrema importância, pois é através dele que “injetaremos” os serviços que desejamos consumir na nossa Function.
Dica 1: Por boas práticas inclusive recomendadas no livro Clean Code do Robert C. Martin (Cap. 3 – Functions), uma Função deve ser extremamente pequena e no caso das Azure Functions isso não é excessão, lembre sempre que se uma Função tiver mais de um propósito, faça uma refatoração da mesma.
Dica 2: Quando suas Azure Functions estiverem publicadas no Azure, caso o plano escolhido seja o Consumption (mais barato), uma função tem tempo limite de 10 minutos para executar, logo já é mais que um bom motivo para suas funções serem “pequenas”.
Voltando para a nossa função CreatePreson vamos substitui o código padrão pelo código abaixo:
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]HttpRequest req,
[Queue("CreatePerson", Connection = "AzureWebJobsStorage")]IAsyncCollector<string> queueItem,
ILogger log)
{
log.LogInformation("CreatePerson function started a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
await queueItem.AddAsync(requestBody);
log.LogInformation("CreatePerson function finished a request.");
return new OkObjectResult($"Obrigado! Seu registro já esta sendo processado.");
}
Observe que o Visual Studio Code vai indicar um erro, na palavra Queue e na propriedade Connection. Isso deve ao fato de que ele não encontrou as mesmas e portanto precisamos importar o SDK do Azure Storage na nossa aplicação, e para fazer isso, abra o arquivo .csproj do seu projeto e logo abaixo da linha em que ele adiciona a referência do SDK das Azure Functions, adicione a seguinte referência:
Com isso ele vai pedir para fazer o Restore do seu projeto e em seguida os erros deixarão de existir.
Comentando sobre o código da nossa Function, nós basicamente “injetamos” a possibilidade de lidar com as Queues do Azure Storage, e então através da nossa HttpTrigger receberemos o conteúdo contido no body da nossa requisição e então, enfileiraremos esse conteúdo no serviço de mensageria do Azure Storage. Note também, que na propriedade Connection eu passo um valor chamado “AzureWebJobsStorage”, nesse caso, vamos abrir o arquivo local.settings.json e localizar a propriedade “AzureWebJobsStorage” e então, substituir o conteúdo dela para a String de Conexão do nosso Azure Storage Emulator. Caso você possua Mac ou Linux você pode indicar nesse local a String de Conexão do seu Azure Storage no Azure.
Como desejamos fazer um CRUD completo, vou utilizar a classe abaixo (Person.cs) como referência para o nosso cadastro.
using Microsoft.WindowsAzure.Storage.Table;
namespace AzureFunctions
{
public class Person : TableEntity
{
public string Name { get; set; }
public string Email { get; set; }
public Person() { }
}
}
Observe que na classe Person eu já herdo a classe TableEntity para já facilitar a inserção dos nosso dados em um outro serviço do Azure Storage chamado Azure Tables.
Feito isso, vamos testar nosso Function App, e para isso digite o seguinte comando:
func start
Note que ele criou o seguinte endpoint para nós: CreatePerson: [POST] http://localhost:7071/api/CreatePerson e com isso já é possível testar nossa função (para isso recomendo utilizar um Client REST da sua preferência) passando o seguinte body:
{
"name": "Blog do Ericson",
"email": "ericson@email.com"
}
Feito isso abra a ferramenta Microsoft Azure Storage Explorer (instalada previamente no inicio do post) e localize dentro de Storage Account o serviços de Queues e abrindo-o note que foi criada uma Queue chamada CreatePerson. Abra a mesma e note que o body que passamos para o nosso endpoint estará armazenado na fila.
Agora nosso próximo passo será desenfileirar a mensagem e salva-la em um banco de dados, e seguindo nosso exemplo esse banco de dados sera o serviço de Tables (banco de dados NoSQL de chave-valor) do Azure Storage.
Vamos então criar uma nova Azure Function, digitando o novamente o comando abaixo:
func new
Feito isso, novamente aparecerão diversas opções de Azure Functions, e dessa vez escolheremos o tipo QueueTrigger e nós daremos a ela o nome de CreatePersonQueue. Após a criação da mesma substitua o código gerado pelo template pelo código abaixo:
public static void Run(
[QueueTrigger("CreatePerson", Connection = "AzureWebJobsStorage")]string queueItem,
[Table("Person")]CloudTable cloudTable,
ILogger log)
{
log.LogInformation($"CreatePersonQueue trigger function started.");
var data = JsonConvert.DeserializeObject<Person>(queueItem);
data.PartitionKey = "Person";
data.RowKey = Guid.NewGuid().ToString();
var tableOperation = TableOperation.Insert(data);
cloudTable.ExecuteAsync(tableOperation);
log.LogInformation($"CreatePersonQueue trigger function finished.");
}
No código acima, vamos deserializar a mensagem para o tipo Person e em seguida precisamos criar duas propriedades obrigatórias para que se possa utilizar o serviço de Tables do Azure Storage. Explicando de maneira bem resumida, a RowKey teria uma função similar ao de um ID de um banco de dados relacional e a PartitionKey teria a função de ajustar o particionamento automático do serviço de Tables do Azure Storage.
Com isso vamos executar nosso Function App novamente através do comando:
func start
E então abra novamente o Microsoft Azure Storage Explorer e note que a mensagem sumiu da fila, e agora a mesma encontra-se salva na tabela Person (criada automaticamente) dentro do serviço de Tables.
Bom pessoal, e com isso finalizamos a primeira parte do nosso CRUDServerless em que criamos nosso registro. No nosso próximo post vamos trabalhar na edição e exclusão dele.
E para finalizar deixo aqui um convite.
Que tal aprender mais sobre Docker, Kubernetes e a implementação de soluções baseadas em containers utilizando o Microsoft Azure, em um workshop que acontecerá durante um sábado todo (dia 04/04) em São Paulo Capital e implementando um case na prática?
Acesse então o link a seguir para efetuar sua inscrição (já incluso camiseta, emissão de certificado e almoço para os participantes) com o desconto: http://bit.ly/anp-docker-blog-ericson
Nesse post vou demonstrar como exibir o último commit do Git na sua aplicação ASP.NET Core.
Primeiramente clique com o botão direito sobre sobre sua aplicação e clique em Properties, em seguida clique em Build Events e na sequência clique em Edit Pre-build e digite a seguinte linha de código que se encontra no bloco abaixo:
git log -1 > "$(ProjectDir)versao.txt"
O comando acima pega o log do último commit e cria um arquivo chamado versao.txt no diretório base do seu projeto. Observe que utilizei a macro ProjectDir para automaticamente criar meu arquivo com o último log no diretório base do projeto. Caso queira saber mais sobre macros, na tela de Edit Pre-build, clique no botão Macros >> haverão muitas opções que poderão lhe auxiliar, caso queira fazer algo mais elaborado.
Nosso próximo passo será colocar nosso arquivo versao.txt “embutido” na nossa aplicação e para isso, clique sobre o arquivo versao.txt com o botão direito e em seguida clique em Properties. Na opção BuildAction selecione Embedded resource e na opção Copy to Output Directory selecione Copy always.
Por fim, agora vamos ler nosso arquivo versao.txt para exibir dentro da sua aplicação, utilizando o código abaixo:
string gitVersion = string.Empty;
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("SuaAplicacao.versao.txt"))
using (var reader = new StreamReader(stream))
{
gitVersion = reader.ReadToEnd();
}
Note que a variável gitVersion receberá o conteúdo do último commit.
Espero que tenham gostado do artigo e até a próximo!
Ontem (08/01), tirei a certificação AZ-900 – Azure Fundamentals, e vou contar para vocês aqui como foi todo o processo de estudo até a prova.
O que cai na prova?
Abaixo, vou fazer um resumos dos principais assuntos que são cobrados na prova, lembrando que para essa prova você não precisa saber implementa-los de fato, apenas saber como eles funcionam e suas diferenças.
A prova AZ-900 é divida em quatro tópicos de estudo, sendo eles:
Conceitos de Nuvem
Entender a diferença entre IaaS, PaaS e SaaS.
Entender a diferença entre nuvem, privada, pública e híbrida.
Entender a diferença entre CapEx e OpEx.
Entender a diferença entre Elasticidade, Escalabilidade, Disponibilidade, Disaster Recovery e etc.
Serviços do Azure
Entender a arquitetura do Azure (Regiões, Zonas de disponibilidade e etc).
Entender os principais serviços do Azure (Computação, Storage, Dados, Rede e etc).
Entender como funciona o Portal do Azure, Azure Cli, Powershell e Cloud Shell.
Entender a utilidade do Azure Advisor.
Segurança, Privacidade e Conformidade
Entender a diferença entre Network Security Group (NSG), Firewall, Proteção DDoS.
Entender como funciona o Azure Active Directory.
Entender para que serve o Azure Key-Vault
Entender as metodologias de segurança do Azure (Policies, Locks e etc).
Entender as opções de monitoramento e Log (Azure Monitor, Log Analytics e etc).
Entender as diferentes normas de conformidade do Azure variando de acordo com cada país.
Precificação e Suporte
Entender a diferença entre uma assinatura “Free” e em uma assinatura “normal”.
Entender para que serve uma Subscription.
Entender a diferença entre os planos de suporte do Azure (Basic, Standart, Professional e etc).
Entender o SLA do Azure.
Como estudar para a prova?
Primeiramente sugiro fortemente que caso você já não tenha, crie uma conta no Azure, pois ela será fundamental para o aprendizado.
Feito isso, existe um guia de estudo que a própria Microsoft disponibilizou, contendo os principais tópicos abordados no exame. Caso você já tenha familiaridade com os conceitos de Cloud Computing e com o Azure, esse guia deve ser suficiente para que você passe na prova, entretanto se você ainda não é familiarizado com esse conceitos sugiro complementar seus estudos com as seguintes opções:
A prova AZ-900 é um pouco mais barata que as demais provas do Azure, custando hoje $60. Essa prova pode ser adquirida através do site da Microsoft Learning.
Quando estiver comprando você terá a opção de escolher entre fazer a prova em um Testing Center ou na sua casa/escritório, eu escolhi fazer na minha casa mesmo e a experiência foi ótima. Claro que existem regras para se fazer o exame na sua casa e elas devem ser seguidas a risca do contrário você perde o exame. Nesse vídeo feito pela própria Microsoft é retratado de maneira bem fiel como funciona todo o processo.
Últimas dicas
Conforme mencionei no inicio do texto, você não vai implementar nada no Azure, a prova é 100% conceitual, logo a palavra-chave aqui para que você tenha sucesso no dia do exame é: entender as diferenças entre os serviços, entre os modelos e etc. Saber qual serviço é mais adequado para cada situação.
Conforme vimos na primeira parte
deste artigo, entendemos brevemente o conceito de como funciona o
protocolo oAuth 2, o OpenID Connect e o Identity Server 4. Então, sem
mais delongas, vamos para a melhor parte, que é colocar tudo isso na
prática.
Show me the Code!
Primeiro vamos criar um novo projeto do tipo ASP.NET Core Web Application (File > New Project). Chamaremos ele de IdentityServer, e na tela seguinte escolheremos a opção Web Application (Model-View-Controller), conforme a figura 1, abaixo:
Feito isso, vamos adicionar o Identity Server4 utilizando o Nuget. Para isso, clique com o botão direito na opção Dependencies e selecione a opção Manage Nuget Packages. Em seguida, adicione o pacote do Identity Server4, conforme figura abaixo:
Agora, para facilitar nosso desenvolvimento, faremos o download do projeto Quickstart UI repo. Esse projeto nos dá uma interface pronta com todos os tipos de autenticação previamente desenvolvidos. Vamos agora copiar as pastas QuickStart, Views e wwwroot baixadas dentro do nosso projeto para que possamos, em seguida, iniciar o desenvolvimento do nosso servidor de identidade.
Depois de copiar todos os arquivos, poderemos deletar do nosso projeto as pastas Controllers e Models, pois não a utilizaremos mais. Com todas essas alterações feitas, seu projeto deve estar igual ao da Figura 3:
Agora vamos definir uma porta para que o nosso projeto execute. Para isso, botão direito em cima do projeto e clique em properties (se for Visual Studio for Mac, clique em options ao invés de properties), depois clique em build, e no campo App URL,
substitua a porta pra 5000 (ou qualquer outra da sua escolha – você só
precisa se lembrar dela depois), e então salve o projeto. Feito isso,
vamos criar uma classe chamada Config.cs e vamos implementar nossas configurações (comentários no próprio código para melhor entendimento):
using IdentityServer4;
using IdentityServer4.Models;
using IdentityServer4.Test;
using System.Collections.Generic;
using System.Security.Claims;
namespace IdentityServer
{
public class Config
{
// Aqui vamos definir os resources que serão utilizados no nosso servidor
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email()
};
}
// Aqui definimos qual Client (aplicação) que poderá acessar nosso servidor de identidade.
public static IEnumerable<Client> GetClients()
{
// Credenciais da Aplicação
return new List<Client>
{
// OpenID Connect
new Client
{
// O Nome ÚNICO da nossa aplicação autorizada no nosso servidor de autoridade
ClientId = "ericsonfApp",
// Nome de exibição da nossa aplicação
ClientName = "ericsonf Application",
//Tipo de autenticação permitida
AllowedGrantTypes = GrantTypes.Implicit,
//Url de redicionamento para quando o login for efetuado com sucesso.
RedirectUris = { "http://localhost:5001/signin-oidc" },
//Url de redirecionamento para quando o logout for efetuado com sucesso.
PostLogoutRedirectUris = { "http://localhost:5001/signout-callback-oidc" },
//Escopos permitidos dentro da aplicação
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email
}
}
};
}
// TestUser é uma classe de exemplo do proprio IdentityServer, aonde configuramos basicamente login/senha e as claims de exibição.
public static List<TestUser> GetUsers()
{
return new List<TestUser>
{
new TestUser
{
SubjectId = "1",
Username = "ericsonf",
Password = "ericsonf",
Claims = new List<Claim>
{
new Claim("name", "Portal ericsonf"),
new Claim("website", "https://ericsonf.com.br"),
new Claim("email", "contato@ericsonf.com")
}
}
};
}
}
}
O próximo passo agora é configurar o Startup.cs da nossa aplicação para que ele fique igual abaixo:
using IdentityServer4.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace IdentityServer
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Configuração do Identity Server In-Memory
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetUsers());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
//Habilitando o uso do Identity Server no nosso projeto
app.UseIdentityServer();
//Habilitando o uso de arquivos estáticos (Html, Css e etc) do nosso projeto
app.UseStaticFiles();
//Habilitando o uso de rota no projeto
app.UseMvcWithDefaultRoute();
}
}
}
Feito isso, configuramos o nosso servidor de Identidade, e agora podemos criar uma aplicação para testarmos a nossa autenticação. Então basicamente vamos seguir os mesmos passos que fizemos anteriormente ao criar um novo projeto do tipo ASP.NET Core Web Application, porém, esse vamos chamar de ericsonfApp. Mais uma vez nossa pasta Models não será utilizada em nosso exemplo, então podemos deletá-la do nosso projeto.
Antes de começarmos a programar nossa Aplicação, vamos primeiramente
configurar a porta em que a aplicação vai rodar, e seguindo os mesmos do
nosso servidor de identidade, vamos alterar a porta, porém dessa vez
vamos apontar para a porta 5001 (observe que no código do Config.cs, apontamos o Login e Logout da aplicação para a porta 5001 também).
Agora podemos começar a programar a nossa aplicação, então
primeiramente vamos abrir o arquivo Startup.cs (comentários no próprio
código para melhor entendimento):
using System.IdentityModel.Tokens.Jwt;
using IdentityServer4;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace ericsonfApp
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
//Exibe as claims de maneira mais "amigável"
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
//Adciona o serviço de autenticação
services.AddAuthentication(options =>
{
//Nosso esquema default será baseado em cookie
options.DefaultScheme = "Cookies";
//Como precisamos recuperar os dados depois do login, utilizamos o OpenID Connect que por padrão utiliza o escopo do Profile
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
//Aponta para o nosso servidor de autenticação
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
//Nome da nossa aplicação que tentará se autenticar no nosso servidor de identidade
//Observe que ela possui o mesmo nome da app que liberamos no nosso servidor de identidade
options.ClientId = "ericsonfApp";
options.SaveTokens = true;
//Adicionamos o scopo do e-mail para utilizarmos a claim de e-mail.
options.Scope.Add(IdentityServerConstants.StandardScopes.Email);
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//Habilitando o uso da autenticação do Identity Server no nosso projeto
app.UseAuthentication();
//Habilitando o uso de arquivos estáticos (Html, Css e etc) do nosso projeto
app.UseStaticFiles();
//Habilitando o uso de rota no projeto
app.UseMvcWithDefaultRoute();
}
}
}
O próximo passo agora será configurarmos nosso HomeController.cs:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication;
namespace MvcClient.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
//Somente será acessada se o usuário estiver logado
[Authorize]
public IActionResult Secure()
{
return View();
}
public async Task Logout()
{
//Limpa o cookie para finalizar o logout
await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("oidc");
}
public IActionResult Error()
{
return View();
}
}
}
E por fim, vamos criar nossa página que vai exibir os dados caso o
usuário seja válido e se logue corretamente. Para isso, crie o arquivo Secure.cshml dentro da pasta Views > Home.
@{
ViewData["Title"] = "Logon efetuado com sucesso.";
}
<h2>@ViewData["Title"]</h2>
<h3>Claims autorizadas pelo usuário</h3>
<dl>
@foreach (var claim in User.Claims)
{
<dt>@claim.Type</dt>
<dd>@claim.Value</dd>
}
</dl>
Feito isso vamos colocar nosso Visual Studio para executar os dois
projetos simultaneamente, e vamos fazer o login com os dados de acesso
que criamos no nosso servidor de identidade. Se tudo deu certo você
obterá uma tela igual a figura 4 abaixo:
Agora é só selecionar quais informações deseja exibir e na próxima tela será exibido o resultado de ac.
Bônus
Como bônus, vou implementar um Resource customizado, pois nem sempre temos as Claims que desejamos “prontas”, então mãos a obra!
Primeiramente vamos alterar o método GetIdentityResources() que se encontra no arquivo Config.cs do projeto IdentityServer.
public static IEnumerable<IdentityResource> GetIdentityResources()
{
var customProfile = new IdentityResource(
name: "custom.profile",
displayName: "Custom Profile",
claimTypes: new[] { "customclaim" }
);
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
customProfile
};
}
Depois, vamos alterar a propriedade AllowedScopes:
Agora vamos adicionar nossa Claim customizada nas Claims do usuário:
Claims = new List<Claim> {
new Claim("name", "Portal ericsonf"),
new Claim("website", "http://ericsonf.com.br"),
new Claim("email", contato@ericsonf.com new Claim("customclaim", "Minha claim customizada")
}
E por ultimo, vamos abrir o arquivo Startup.cs do projeto ericsonfApp e colocar a linha abaixo logo após a inclusão do scopo do e-mail:
options.Scope.Add("custom.profile");
Executando o projeto novamente e fazendo o login corretamente, note que
apareceu o nosso Resource customizado nas opções de exibição, conforme
mostra a figura 5 abaixo:
Bom, pessoal, era isso que eu queria mostrar. Para quem quiser o código completo ele está no meu GitHub.