Design do banner por Freepik

Replicando Repositórios do Bitbucket para o AWS CodeCommit via SSH com o Bitbucket Pipelines

Miller Horvath
BRLink
Published in
14 min readNov 1, 2023

--

O objetivo deste artigo é apresentar o passo-a-passo de uma alternativa simples para replicar o código de branches específicas de um repositório no Bitbucket para o AWS CodeCommit.

Autores

Exemplos de Caso de Uso

Creio que você esteja se perguntando: “mas em que cenários este tipo de replicação pode ser útil?”. Por isso, antes de entrarmos nos detalhes de como realizar essa replicação, vamos trazer algumas situações práticas nas quais utilizamos esse processo.

Evitar a Criação de Chaves de Acesso Externo da AWS

Vamos imaginar um contexto onde um projeto deve ser implantado na cloud AWS, com o versionamento de código mantido no Bitbucket.

Temos várias opções de ferramentas para realizar essa implantação, como o Serverless Framework, por exemplo. Porém, muitas vezes essas ferramentas exigem a criação de Access Keys (IAM Users), com certo nível de permissionamento no ambiente AWS para criar todos os recursos necessários. O vazamento dessas chaves gera um risco para o ambiente, sendo que a criação e/ou exclusão indesejada de recursos pode acarretar na geração indevida de custos, vazamento de informações sensíveis ou indisponibilidade da aplicação.

Na solução apresentada neste artigo, o acesso externo tem permissão apenas para fazer o Push de código para um repositório específico, não mais via Access Key, mas com uma chave SSH pré-configurada. Com isso, a criação de recursos AWS fica restrita ao ambiente, controlada através de IAM Roles.

Evitar Conexão Direta ao Seu Repositório em Ambientes AWS de Terceiros

As ferramentas de CI/CD da AWS permitem que você crie uma conexão direta com uma plataforma externa de versionamento de código. O que pode ser muito prático para projetos pessoais, mas não muito interessante para projetos que envolvam equipes.

Vamos imaginar um cenário em que um desenvolvedor do time faz a integração do repositório da empresa (digamos o Bitbucket) com o fluxo de implantação de um projeto interno muito sensível para a companhia. Se esse desenvolvedor sair da empresa e seus acessos forem excluídos, o processo de implantação da aplicação para.

Poderia-se contornar isto criando uma credencial única para as integrações, mas daí seria necessário compartilhar as credenciais entre muitas pessoas, o que não é recomendado. Ou então, poderia-se criar vários acessos para cada integração, o que geraria uma complicação no gerenciamento dessas credenciais.

O mesmo se aplica a projetos com clientes externos, que ficariam com o fluxo de implantação dependente da pessoa que configurou as integrações com suas credenciais próprias. Um fator agravante, neste caso, é que o vínculo com um cliente pode não ser eterno (normalmente não é). Daí, uma vez que você deixe de atender o cliente, não poderá esquecer de remover a integração com o repositório, além de muitas vezes ter de encontrar uma alternativa para compartilhar o código-fonte com os mesmos. O que nos leva ao nosso próximo cenário…

Replicação de Código-Fonte para Clientes

É muito comum que empresas e consultores autônomos concentrem o versionamento de código dos seus projetos desenvolvidos em uma plataforma centralizada. Contudo, é também comum que o código-fonte produzido seja um dos artefatos a serem entregues ao cliente no fim do projeto.

Portanto, uma forma simples de realizar essa entrega é replicando o código do seu repositório de origem para um repositório controlado pelo cliente.

Esses foram alguns exemplos onde a integração entre o Bitbucket e o CodeCommit foram úteis para nós.

Caso este conteúdo tenha te ajudado a resolver algum desafio, principalmente se for diferente dos listados aqui, responda (💬) ao nosso artigo. Queremos ler os seus comentários! 🙂

Agora, sem mais delongas, vamos ao como.

Configurando a Replicação — Passo-a-Passo

Vamos apresentar aqui uma série de etapas necessárias para realizar esta replicação, fazendo com que atualizações em branches específicas do repositório do Bitbucket sejam espelhadas para o repositório do AWS CodeCommit via SSH.

Ativar o Bitbucket Deployment (Primeira Vez na Conta)

A partir do seu repositório de origem no Bitbucket (seja ele recém criado ou com centenas de milhares de commits), o primeiro passo é ativar a utilização do Bitbucket Pipelines. O caminho para a ativação da funcionalidade pode ser diferente, dependendo se você já utilizou essa funcionalidade alguma vez na sua conta do Bitbucket.

Vale reforçar que, para realizar a ativação e configuração desta funcionalidade, é necessário ser administrador do repositório.

Caso nunca tenha utilizado, as opções do Bitbucket Pipelines ficam indisponíveis nas configurações do repositório. Para ativá-la, será necessário fazer o commit de um arquivo de configuração do Bitbucket Pipelines, chamado bitbucket-pipelines.yml. Por enquanto, podemos apenas criar um arquivo padrão a partir da própria interface, para isso, você deverá:

  1. Estando na página do seu repositório de origem, selecionar a opção Deployments no menu lateral.
  2. Clique no botão Commit file.
Criando o primeiro arquivo de configuração do Bitbucket Pipelines.

Após incluir o arquivo bitbucket-pipelines.yml, se você voltar a visitar a página de Deployments, ela terá outras opções. Nesse caso:

  1. Estando na página do seu repositório de origem, selecionar a opção Deployments no menu lateral.
  2. Clique no botão Enable.
Ativando o Bitbucket Deployment.

Após esta primeira configuração, a opção Pipelines ficará disponível nas configurações do repositório — pode ser necessário dar um refresh (F5) ou um hard-refresh (Ctrl + F5) na página.

Ativar o Bitbucket Pipelines

Caso já tenha feito a etapa anterior em algum repositório na sua conta, a opção de Pipelines já deverá estar disponível nas configurações do repositório. Para ativar o Bitbucket Pipelines:

  1. Estando na página do seu repositório de origem, selecionar a opção Deployments no menu lateral.
  2. Navegue no menu lateral até encontrar a opção PIPELINES → Settings e clique nesta opção.
  3. Em seguida, ative o checkbox referente ao item Enable Pipelines.
Acessando as opções de configuração do repositório.
Acessando as configurações do Bitbucket Pipelines e ativando-o.

Gerar Par de Chaves SSH

O próprio Bitbucket Pipelines possuí um recurso para criar e manter pares de chaves SSH, além de definir os hosts conhecidos para que os fluxos de implantação possam se conectar com serviços externos. Neste método, temos acesso apenas à chave pública SSH, uma vez que a chave privada é mantida pelo Bitbucket.

Para gerar as chaves, estando ainda em Repository Settings:

  1. Navegue no menu lateral até encontrar PIPELINES → SSH Keys e clique nesta opção.
  2. Clique no botão Generate Keys.
  3. Em seguida, a tela será atualizada com as informações do par de chaves SSH que foram geradas. Com isso, basta clicar no botão Copy public key e guardar essa informação, pois será necessária nas etapas subsequentes.
Acessando as configurações de chaves SSH do Bitbucket Pipelines e criando um par de chaves.
Copiando a chave SSH pública gerada pelo Bitbucket.

Na mesma tela de SSH keys, devemos registrar os hosts conhecidos que terão a comunicação com o repositório autorizada. Para isso:

  1. Adicione o host do CodeCommit na caixa de texto Host address, seguindo o padrão:
    git-codecommit.<aws_region>.amazonaws.com
    Substitua o <aws_region> pela região na qual o repositório do CodeCommit foi (ou será) criado. No caso deste exemplo, estamos utilizando a região us-east-1 (Norte da Virgínia).
  2. Clique no botão Fetch para gerar o fingerprint do host.
  3. Após a geração do fingerprint do host, clique no botão Add host para adicionar o CodeCommit nos hosts conhecidos.
Gerando o fingerprint do CodeCommit como host.
Adicionando o CodeCommit na lista de hosts conhecidos.

Para mais detalhes sobre o funcionamento das chaves SSH nos pipelines do Bitbucket, acesse:

Criar Usuário na AWS para Integração com o CodeCommit

Nesta etapa, vamos criar e configurar um IAM User no ambiente onde o repositório de destino no AWS CodeCommit esteja criado.

Aqui vamos colocar de forma simplificada para aqueles que já estão familiarizados com a criação de recursos do AWS IAM. Caso tenha dificuldade neste processo, você pode acompanhar o passo-a-passo da criação no seguinte artigo:

Seguem as etapas para a criação do usuário:

  • Crie uma IAM Policy com as definições das permissões necessárias para o usuário em questão. Segue um documento JSON de referência, sendo necessário apenas substituir os seguintes elementos:
    <AWS Region> → utilizar a mesma região na qual o repositório do CodeCommit foi criado.
    <AWS Account ID> → utilizar o ID da conta AWS na qual está trabalhando (representada por 12 dígitos numéricos)
    <Repository Name> → utilizar o nome dos repositórios do CodeCommit que serão integrados. No campo Resource, você pode também especificar um conjunto de repositórios com algum padrão de de sufixo/prefixo utilizando o wildcard “*”.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CodeCommitPermissions",
"Effect": "Allow",
"Action": [
"codecommit:GitPush"
],
"Resource": [
"arn:aws:codecommit:<AWS Region>:<AWS Account ID>:<Repository Name>"
]
}
]
}
  • Crie um usuário.
    — No tipo de acesso, por se tratar de uma informação obrigatória para a criação de usuário via console, marque a checkbox de acesso programático. Vamos excluir as chaves de acesso geradas logo após a criação do usuário, pois as mesmas não são necessárias.
    — Na definição de permissões, anexe a IAM Policy criada na etapa anterior.
  • Após a criação do usuário, navegue para a tela de configurações deste usuário. Na aba “Credenciais de segurança“, desative e exclua as Access Keys geradas automaticamente.
  • Ainda na aba “Credenciais de Segurança“, faça o upload da chave pública SSH. Copie e cole a chave pública SSH gerada pelo Bitbucket nas etapas anteriores. Ao realizar o upload, será gerado um ID da Chave do SSH, guarde esse ID, pois ele será necessário posteriormente.

Configuração do Bitbucket Pipelines

Nesta etapa, vamos configurar as variáveis de ambiente do Bitbucket Pipelines para relacionar corretamente as branches de cada stage (dev, hml, prd, etc.) com o seu respectivo repositório do AWS CodeCommit. Além disso, também vamos definir a configuração de execução do pipeline do Bitbucket, através de um arquivo YAML.

Temos 2 tipos de variáveis de ambiente, as variáveis de repositório (que servem para qualquer ambiente/stage) e as variáveis de deploy (que são definidas para um stage específico do deploy).

Neste exemplo, vamos precisar configurar 4 variáveis.

  • SSH_KEY_ID → ID da chave SSH, gerada ao realizar o upload da chave pública para o usuário do IAM que criamos anteriormente. Utilizada como usuário para estabelecer a conexão com o repositório do CodeCommit via SSH.
  • CODECOMMIT_HOST → Host utilizado para estabelecer a comunicação com o repositório do CodeCommit via SSH. Seguindo o padrão:
    git-codecommit.<aws_region>.amazonaws.com
  • CODECOMMIT_REPO → URL SSH para o repositório destino no CodeCommit. Para obter a URL do repositório:
    — Navegue na console AWS até a página do repositório do CodeCommit que foi criado para o projeto.
    — Clique em Clonar URL → Clonar SSH para copiar a URL.
  • REMOTE_BRANCH_NAME → Nome da branch de destino no repositório do AWS CodeCommit. É uma alternativa opcional, caso queira que o nome da branch no AWS CodeCommit seja diferente do nome da branch. Mais pra frente, vou mostrar uma alternativa sem esta variável, de modo a preservar o nome da branch em ambos os repositórios.

Cada uma delas deverá ser definida como variável de repositório ou variável de deploy depende da forma como a aplicação será implantada.

Por exemplo, vamos supor que você tenha contas AWS diferentes para os ambientes de desenvolvimento e produção da sua aplicação, rodando em regiões diferentes da AWS, digamos que uma em Virgínia (us-east-1) e uma em São Paulo (sa-east-1). Neste cenário, todas as variáveis precisam ser de deploy, uma vez que são diferentes para cada ambiente.

Em contrapartida, vamos imaginar um cenário em que os ambientes de dev e prod da aplicação estão na mesma conta AWS, na mesma região, utilizando o mesmo repositório do CodeCommit, só com a branch diferente. Neste caso, apenas a variável REMOTE_BRANCH_NAME precisar ser de deploy, as demais podem ser de repositório.

Iremos abordar essas possibilidades de configuração a seguir.

Variáveis de Repositório

Para cadastrar uma variável de repositório:

  1. Estando na tela de Repository Settings, navegue no menu lateral até encontrar PIPELINES → Repository variables e clique nesta opção.
  2. Preencha o campo Name para definir o nome da variável.
  3. Preencha o campo Value para definir o valor que será armazenado na variável.
    Para dados sensíveis, o valor pode ser mascarado, para que ele não possa ser visualizado através da interface do Bitbucket. Para mascarar o valor da variável, mantenha a checkbox Secured ativada.
  4. Clique no botão Add para adicionar a variável de repositório.

Variável de Deploy

Primeiro, devemos selecionar qual ambiente de deploy queremos configurar. Para isso:

  1. Estando na tela de Repository Settings, navegue no menu lateral até encontrar PIPELINES → Deployments e clique nesta opção.
  2. (opcional) O Bitbucket cria alguns ambientes de deploy por padrão: (Test, Staging e Production). Porém, podemos adicionar novos ambientes de deploy conforme necessário. Para isso, clique no botão +, preencha o campo Environment name com o nome do ambiente que deseja criar e clique no botão de confirmação para adicionar o novo ambiente de deploy.
  3. Clique no ambiente de deploy que deseja configurar para seguir para a próxima etapa.

Para adicionar variáveis de deploy:

  1. Preencha o campo Name para definir o nome da variável.
  2. Preencha o campo Value para definir o valor que será armazenado na variável.
    Assim como para as variáveis de repositório, as variáveis de deploy também podem ter o valor mascarado através da checkbox Secured.
  3. Clique no botão Add para adicionar a variável de deploy.

Configuração do Bitbucket Pipelines

O Bitbucket Pipelines é um recurso para realizar execução de comandos com base nas configurações definidas para o repositório, com o objetivo de automatizar build, teste e deploy de aplicações. Para mais informações sobre o Bitbucket Pipelines, acesse:

Esta configuração é realizada através de um arquivo YAML, chamado bitbucket-pipelines.yml, armazenado na raiz do repositório. Neste documento, vamos definir:

  • Quais branches do repositório vão disparar um pipeline do Bitbucket.
  • Qual ambiente de deploy (que contém as varáveis de deploy configuradas anteriormente) que deverá ser utilizado para cada branch.
  • Quais comandos deverão ser executados pelo pipeline para cada branch.

O repositório do Bitbucket usado para este exemplo foi o seguinte:

No nosso exemplo, vamos configurar a execução do pipeline para as branches dev e prd. Seguindo o código a seguir:

image: 'atlassian/default-image:2'
pipelines:
branches:
dev:
- step:
name: Update Development Stage CodeCommit Repo
deployment: Development
script:
- chmod +x bitbucket-push-to-codecommit.sh # Make bitbucket-push-to-codecommit.sh executable
- ./bitbucket-push-to-codecommit.sh # Execute bitbucket-push-to-codecommit.sh script
prd:
- step:
name: Update Production Stage CodeCommit Repo
deployment: Production
script:
- chmod +x bitbucket-push-to-codecommit.sh # Make bitbucket-push-to-codecommit.sh executable
- ./bitbucket-push-to-codecommit.sh # Execute bitbucket-push-to-codecommit.sh script

Como os comandos à serem executados são idênticos para ambos os stages, variando apenas os valores das variáveis de ambiente, os comandos foram concentrados em um script em bash (que chamamos de bitbucket-push-to-codecommit.sh).

O script em bash para concentrar os comandos a serem executados foi criado única e exclusivamente por uma questão de organização. Sendo que os comandos poderiam ser definidos diretamente do YAML de configuração do pipeline.

Segue o script em bash utilizado:

#!/usr/bin/env sh

set -eu # Exit script on error

echo "Host $CODECOMMIT_HOST" >> ~/.ssh/config # Set host on ssh config file
echo "User $SSH_KEY_ID" >> ~/.ssh/config # Set user on ssh config file
git remote add codecommit $CODECOMMIT_REPO # Add CodeCommit repo as git remote
git push -u codecommit $BITBUCKET_BRANCH:$REMOTE_BRANCH_NAME # Push source branch into the predefined CodeCommit repo branch

Conforme comentado anteriormente, caso queira sempre manter o mesmo nome nas branches de origem e destino, pode desconsiderar a variável REMOTE_BRANCH_NAME e utilizar o seguinte scrip em bash:

#!/usr/bin/env sh

set -eu # Exit script on error

echo "Host $CODECOMMIT_HOST" >> ~/.ssh/config # Set host on ssh config file
echo "User $SSH_KEY_ID" >> ~/.ssh/config # Set user on ssh config file
git remote add codecommit $CODECOMMIT_REPO # Add CodeCommit repo as git remote
git push -u codecommit $BITBUCKET_BRANCH # Push source branch into a CodeCommit branch with the same name

Com isso, os pushes realizados nas branches dev e prd devem resultar na execução do Bitbucket Pipeline responsável por refletir os pushes para os respectivos repositórios do AWS CodeCommit.

Resolvendo Problemas Conhecidos

Nesta sessão, vamos apresentar alguns problemas que nos deparamos ao implementar essa integração e eles foram contornados.

Convidamos você mais uma vez a responder (💬) aqui no artigo caso tenha se deparado com um problema diferente dos que apresentamos aqui. Compartilhe também a sua solução, pois pode acabar ajudando mais pessoas.

unpack failed: Unknown commit

Quando se está configurando a replicação de um repositório de origem já com um grande histórico de commits, você pode se deparar com o seguinte erro no Bitbucket Pipelines:

error: unpack failed: Unknown commit <commit_id_hash>

Para contornar esse problema, você deve realizar o primeiro push manualmente do repositório de origem para o de destino. Você deve conseguir fazer isso seguindo basicamente o script apresentado aqui para adicionar o remote do AWS CodeCommit e fazer o push, mas executando isso a partir do seu ambiente de trabalho local. O passo-a-passo para resolver é o seguinte:

  1. Clonar o repositório do Bitbucket no seu ambiente local
  2. Adicionar o CodeCommit como origem no seu ambiente local
    git remote add codecommit $CODECOMMIT_REPO
  3. Realizar o merge da branch de origem (Bitbucket) e de destino (CodeCommit) configurada no Bitbucket Pipelines
  4. Realizar o push para a branch de destino (CodeCommit)

Pronto, a partir daqui as próximas execuções do Bitbucket Pipelines devem funcionar corretamente. Seguem algumas referências de como configurar o ambiente local com o CodeCommit e Bitbucket…

Configurando ambiente local para o CodeCommit

Para configurar o ambiente local para acessar tanto o CodeCommit, você deverá criar um Usuário do IAM com as permissões necessárias para o seu repositório, além de configurar o acesso via HTTPS ou SSH, conforme preferir. Seguem as referências da AWS de como fazê-lo:

Configurando ambiente local para o Bitbucket

Assim como com o CodeCommit, o Bitbucket fornece alguns materiais de referência de como se conectar por SSH ou pelo Git Credential Manager. Segue:

--

--

Miller Horvath
BRLink
Editor for

Data Science Chapter Lead at BRLink; M.S. in Computer Science at Centro Universitário FEI; AWS builder; and researcher in ML.